diff --git a/services/static-webserver/client/source/class/osparc/About.js b/services/static-webserver/client/source/class/osparc/About.js index 803d5caa9e8..2b1781dc23f 100644 --- a/services/static-webserver/client/source/class/osparc/About.js +++ b/services/static-webserver/client/source/class/osparc/About.js @@ -184,27 +184,6 @@ qx.Class.define("osparc.About", { const label = lib.name || "unknown-library"; const version = lib.version || "-"; const url = lib.url || null; - const thumbnail = lib.thumbnail || null; - - if (thumbnail) { - const image = new qx.ui.basic.Image(thumbnail).set({ - height: 30, - maxWidth: this.self().MAX_WIDTH - 2*this.self().PADDING, - scale: true, - toolTipText: label + (version === "-" ? "" : (" " + version)) - }); - image.getContentElement().setStyles({ - "object-fit": "contain", - "object-position": "left" - }); - if (url) { - image.set({ - cursor: "pointer" - }); - image.addListener("tap", () => window.open(url)); - } - return [image]; - } let entryLabel; if (url) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js index 5322e8e8d67..446ee83f886 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js @@ -109,17 +109,12 @@ qx.Class.define("osparc.dashboard.ContainerHeader", { const workspace = osparc.store.Workspaces.getWorkspace(workspaceId); rootButton = new qx.ui.form.Button(workspace.getName(), osparc.store.Workspaces.iconPath()); } - rootButton.addListener("execute", () => this.set({ - currentWorkspaceId: workspaceId, - currentFolderId: null, - })); } else { rootButton = new qx.ui.form.Button(this.tr("My Workspace"), "@FontAwesome5Solid/home/14"); - rootButton.addListener("execute", () => this.set({ - currentWorkspaceId: null, - currentFolderId: null, - })); } + rootButton.addListener("execute", () => this.set({ + currentFolderId: null, + })); return rootButton; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index 9db4130e02b..5a717abe358 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -58,6 +58,12 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { apply: "__applyFolder" }, + workspaceId: { + check: "Number", + nullable: true, + apply: "__applyWorkspaceId" + }, + folderId: { check: "Number", nullable: false @@ -87,8 +93,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { let control; switch (id) { case "icon": { - control = new qx.ui.basic.Image().set({ - source: "@FontAwesome5Solid/folder/26", + control = new osparc.dashboard.FolderWithSharedIcon().set({ anonymous: true, height: 40, padding: 5 @@ -136,6 +141,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { this.set({ cardKey: "folder-" + folder.getFolderId() }); + folder.bind("workspaceId", this, "workspaceId"); folder.bind("folderId", this, "folderId"); folder.bind("parentId", this, "parentFolderId"); folder.bind("name", this, "title"); @@ -144,6 +150,22 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { this.__addMenuButton(); }, + __applyWorkspaceId: function(workspaceId) { + const workspace = osparc.store.Workspaces.getWorkspace(workspaceId); + const accessRights = workspace ? workspace.getAccessRights() : {}; + if (accessRights && Object.keys(accessRights).length) { + const shareIcon = this.getChildControl("icon").getChildControl("shared-icon"); + // if it's not shared don't show the share icon + shareIcon.addListener("changeSource", e => { + const newSource = e.getData(); + shareIcon.set({ + visibility: newSource.includes(osparc.dashboard.CardBase.SHARE_ICON) ? "hidden" : "visible" + }); + }); + osparc.dashboard.CardBase.populateShareIcon(shareIcon, accessRights); + } + }, + __applyTitle: function(value) { const label = this.getChildControl("title"); label.setValue(value); @@ -172,7 +194,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { moveToFolderButton.addListener("execute", () => this.fireDataEvent("moveFolderToFolderRequested", this.getFolderId()), this); menu.add(moveToFolderButton); - const moveToWorkspaceButton = new qx.ui.menu.Button(this.tr("Move to Workspace..."), ""); + const moveToWorkspaceButton = new qx.ui.menu.Button(this.tr("Move to Workspace..."), osparc.store.Workspaces.iconPath(14)); moveToWorkspaceButton.addListener("execute", () => this.fireDataEvent("moveFolderToWorkspaceRequested", this.getFolderId()), this); menu.add(moveToWorkspaceButton); @@ -199,7 +221,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { label: folder.getName(), }); const title = this.tr("Edit Folder"); - const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 150); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 120); folderEditor.addListener("updateFolder", () => { const newName = folderEditor.getLabel(); const updateData = { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js index 1f0e862c726..627ff5ccab9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js @@ -80,7 +80,7 @@ qx.Class.define("osparc.dashboard.FolderButtonNew", { const newFolder = true; const folderEditor = new osparc.editor.FolderEditor(newFolder); const title = this.tr("New Folder"); - const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 150); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 120); folderEditor.addListener("createFolder", () => { const name = folderEditor.getLabel(); this.fireDataEvent("createFolder", { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js deleted file mode 100644 index d3873d37b08..00000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js +++ /dev/null @@ -1,41 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2024 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Class.define("osparc.dashboard.FolderTreeItem", { - extend: qx.ui.tree.VirtualTreeItem, - - members: { - _addWidgets: function() { - this.addSpacer(); - // this.addOpenButton(); - const openButton = this.getChildControl("open"); - openButton.addListener("changeOpen", () => { - console.log("changeOpen", this); - }, this); - openButton.addListener("changeVisibility", e => { - // console.log("changeVisibility", this.getLabel() + e.getData(), this); - openButton.show(); - }, this); - this._add(openButton); - this.addIcon(); - const label = this.getChildControl("label"); - this._add(label, { - flex: 1 - }); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js similarity index 95% rename from services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js rename to services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js index 2e3b8a88d89..af6573fb16f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.dashboard.WorkspaceWithSharedIcon", { +qx.Class.define("osparc.dashboard.FolderWithSharedIcon", { extend: qx.ui.core.Widget, construct: function() { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 059a01c49f2..0e20d800be6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -20,21 +20,19 @@ qx.Class.define("osparc.dashboard.FoldersTree", { construct: function(currentWorkspaceId) { this.__currentWorkspaceId = currentWorkspaceId; - const workspace = osparc.store.Workspaces.getWorkspace(currentWorkspaceId); - const rootLabel = workspace ? workspace.getName() : "My Workspace"; - const rootFolder = this.self().createNewEntry(rootLabel, null); - const root = qx.data.marshal.Json.createModel(rootFolder, true); - this.__fetchChildren(root); - this.base(arguments, root, "label", "children"); + const workspace = osparc.store.Workspaces.getWorkspace(this.__currentWorkspaceId); + const workspaceLabel = workspace ? workspace.getName() : "My Workspace"; + const rootData = { + label: workspaceLabel, + folderId: null, + children: [], + loaded: true, + }; + const root = qx.data.marshal.Json.createModel(rootData, true); + this.__populateFolder(root); - this.set({ - openMode: "dbltap", - decorator: "no-border", - font: "text-14", - showLeafs: true, - paddingLeft: -10, - }); + this.base(arguments, root, "label", "children"); this.__initTree(); }, @@ -43,94 +41,48 @@ qx.Class.define("osparc.dashboard.FoldersTree", { "selectionChanged": "qx.event.type.Event" // tap }, - statics: { - createNewEntry: function(label, folderId) { - return { - label, - folderId, - children: [ - this.self().getLoadingData() - ], - loaded: false, - }; - }, - - getLoadingData: function() { - return { - folderId: -1, - label: "Loading...", - children: [], - icon: "@FontAwesome5Solid/circle-notch/12", - loaded: false, - }; - }, - - addLoadingChild: function(parentModel) { - const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); - parentModel.getChildren().append(loadingModel); - }, - - removeLoadingChild: function(parent) { - for (let i = parent.getChildren().getLength() - 1; i >= 0; i--) { - if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { - parent.getChildren().splice(i, 1); - } - } - } - }, - members: { __currentWorkspaceId:null, __initTree: function() { const that = this; this.setDelegate({ - createItem: () => new osparc.dashboard.FolderTreeItem(), bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); - c.bindProperty("folderId", "model", null, item, id); c.bindProperty("", "open", { - converter(value, _, __, target) { + converter(value, model, source, target) { const isOpen = target.isOpen(); if (isOpen && !value.getLoaded()) { + value.setLoaded(true); // eslint-disable-next-line no-underscore-dangle - that.__fetchChildren(value); + that.__populateFolder(value); } return isOpen; - } + }, }, item, id); }, configureItem: item => { - item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel()), this); + item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel().getFolderId()), this); }, - sorter: (a, b) => { - const aLabel = a.getLabel(); - if (aLabel === -1) { - return 1; - } - const bLabel = b.getLabel(); - if (bLabel === -1) { - return -1; - } - return aLabel - bLabel; - } }); }, - __fetchChildren: function(parentModel) { - parentModel.setLoaded(true); - - const folderId = parentModel.getFolderId ? parentModel.getFolderId() : parentModel.getModel(); - osparc.store.Folders.getInstance().fetchFolders(folderId, this.__currentWorkspaceId) + __populateFolder: function(parent) { + osparc.store.Folders.getInstance().fetchFolders(parent.getFolderId(), this.__currentWorkspaceId) .then(folders => { - this.self().removeLoadingChild(parentModel); + parent.getChildren().removeAll(); folders.forEach(folder => { - const folderData = this.self().createNewEntry(folder.getName(), folder.getFolderId()); - const folderModel = qx.data.marshal.Json.createModel(folderData, true); - parentModel.getChildren().append(folderModel); + const folderData = { + label: folder.getName(), + folderId: folder.getFolderId(), + loaded: false, + children: [{ + label: "Loading...", + }] + }; + parent.getChildren().push(qx.data.marshal.Json.createModel(folderData, true)); }); - }) - .catch(console.error); - } + }); + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js index 56e9d5a674f..ade091db85c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js @@ -33,8 +33,12 @@ qx.Class.define("osparc.dashboard.MoveResourceToWorkspace", { moveButton.setEnabled(false) workspacesTree.addListener("selectionChanged", e => { const workspaceId = e.getData(); - moveButton.setEnabled(this.__currentWorkspaceId !== workspaceId); - this.__selectedWorkspaceId = workspaceId; + if (this.__currentWorkspaceId !== workspaceId && workspaceId !== -1) { + moveButton.setEnabled(true); + this.__selectedWorkspaceId = workspaceId; + } else { + moveButton.setEnabled(false); + } }); moveButton.addListener("execute", () => { this.fireDataEvent("moveToWorkspace", this.__selectedWorkspaceId); @@ -48,6 +52,7 @@ qx.Class.define("osparc.dashboard.MoveResourceToWorkspace", { members: { __currentWorkspaceId: null, + __selectedWorkspaceId: null, _createChildControlImpl: function(id) { let control; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js index 4a93a2b0d00..b4f4a70e301 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js @@ -157,7 +157,8 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { osparc.store.Workspaces.fetchWorkspaces() .then(workspaces => { workspaces.forEach(workspace => { - const workspaceButton = new qx.ui.toolbar.RadioButton(workspace.getName(), osparc.store.Workspaces.iconPath(22)); + const workspaceButton = new qx.ui.toolbar.RadioButton(null, osparc.store.Workspaces.iconPath(22)); + workspace.bind("name", workspaceButton, "label"); workspaceButton.workspaceId = workspace.getWorkspaceId(); this.__workspaceButtons.push(workspaceButton); workspaceButton.set({ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 1e288034fbc..95a8583788d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -62,7 +62,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { nullable: true, init: null, event: "changeCurrentFolderId", - apply: "__applyCurrentFolderId" + apply: "__resetAndReloadAll" }, multiSelection: { @@ -145,7 +145,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { reloadResources: function() { if (osparc.data.Permissions.getInstance().canDo("studies.user.read")) { - this.__reloadFoldersAndStudies(); + this.__reloadStudies(); } else { this.__resetStudiesList(); } @@ -403,12 +403,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesList = []; this.__reloadWorkspaces(); + } else if (this.getCurrentFolderId()) { + // this will also trigger the __resetAndReloadAll + this.setCurrentFolderId(null); } else { - this._resourcesContainer.setResourcesToList([]); - this._resourcesList = []; - this.invalidateStudies(); - - this.__reloadFoldersAndStudies(); + this.__resetAndReloadAll(); } } }, @@ -451,26 +450,22 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, // /WORKSPACES - // FOLDERS - __applyCurrentFolderId: function() { + __resetAndReloadAll: function() { if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { - const folderId = this.getCurrentFolderId(); const workspaceId = this.getCurrentWorkspaceId(); if (workspaceId === -1) { return; } - osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId) - .then(() => { - this._resourcesContainer.setResourcesToList([]); - this._resourcesList = []; - this.invalidateStudies(); - this.__reloadFoldersAndStudies(); - }) - .catch(console.error); + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + this.invalidateStudies(); + + this.__reloadFoldersAndStudies(); } }, + // FOLDERS __reloadFolderCards: function() { this._resourcesContainer.setFoldersToList(this.__foldersList); this._resourcesContainer.reloadFolders(); @@ -520,12 +515,25 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; osparc.data.Resources.fetch("folders", "moveToFolder", params) .then(() => { + const folder = osparc.store.Folders.getInstance().getFolder(folderId); + if (folder) { + folder.setFolderId(destFolderId); + } this.__reloadFolders() }) .catch(err => console.error(err)); }); }, + __showMoveToWorkspaceWarningMessage: function() { + const msg = this.tr("The permissions will be taken from the new workspace?"); + const win = new osparc.ui.window.Confirmation(msg).set({ + confirmText: this.tr("Move"), + }); + win.open(); + return win; + }, + _moveFolderToWorkspaceRequested: function(folderId) { const moveFolderToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentWorkspaceId()); const title = "Move to Workspace"; @@ -533,18 +541,27 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { moveFolderToWorkspace.addListener("moveToWorkspace", e => { win.close(); const destWorkspaceId = e.getData(); - const params = { - url: { - folderId, - workspaceId: destWorkspaceId, + const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + const params = { + url: { + folderId, + workspaceId: destWorkspaceId, + } + }; + osparc.data.Resources.fetch("folders", "moveToWorkspace", params) + .then(() => { + const folder = osparc.store.Folders.getInstance().getFolder(folderId); + if (folder) { + folder.setWorkspaceId(destWorkspaceId); + } + this.__reloadFolders() + }) + .catch(err => console.error(err)); } - }; - osparc.data.Resources.fetch("folders", "moveToWorkspace", params) - .then(() => { - this.__reloadFolders() - }) - .catch(err => console.error(err)); - }); + }, this); + }, this); }, _deleteFolderRequested: function(folderId) { @@ -609,12 +626,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); osparc.store.Store.getInstance().addListener("changeTags", () => { this.invalidateStudies(); - this.reloadResources(); + this.__reloadStudies(); }, this); qx.event.message.Bus.subscribe("reloadStudies", () => { this.invalidateStudies(); - this.reloadResources(); + this.__reloadStudies(); }, this); }, @@ -843,6 +860,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // LAYOUT // _createLayout: function() { + if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { + const workspaceHeader = new osparc.dashboard.WorkspaceHeader(); + this.bind("currentWorkspaceId", workspaceHeader, "currentWorkspaceId"); + this._addToLayout(workspaceHeader); + } + this._createResourcesLayout(); const containerHeader = this._resourcesContainer.getContainerHeader(); @@ -850,9 +873,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.bind("currentWorkspaceId", containerHeader, "currentWorkspaceId", { onUpdate: () => containerHeader.setCurrentFolderId(null) }); - containerHeader.bind("currentWorkspaceId", this, "currentWorkspaceId", { - onUpdate: () => this.setCurrentFolderId(null) - }); this.bind("currentFolderId", containerHeader, "currentFolderId"); containerHeader.bind("currentFolderId", this, "currentFolderId"); } @@ -1317,6 +1337,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; osparc.data.Resources.fetch("studies", "moveToFolder", params) .then(() => { + studyData["folderId"] = destFolderId; this.__removeFromStudyList(studyData["uuid"]); }) .catch(err => { @@ -1335,25 +1356,31 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { moveToWorkspaceButton["moveToWorkspaceButton"] = true; moveToWorkspaceButton.addListener("tap", () => { const title = this.tr("Move") + " " + studyData["name"]; - const moveStudyToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentFolderId()); + const moveStudyToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentWorkspaceId()); const win = osparc.ui.window.Window.popUpInWindow(moveStudyToWorkspace, title, 350, 280); moveStudyToWorkspace.addListener("moveToWorkspace", e => { win.close(); const destWorkspaceId = e.getData(); - const params = { - url: { - studyId: studyData["uuid"], - workspaceId: destWorkspaceId, + const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + const params = { + url: { + studyId: studyData["uuid"], + workspaceId: destWorkspaceId, + } + }; + osparc.data.Resources.fetch("studies", "moveToWorkspace", params) + .then(() => { + studyData["workspaceId"] = destWorkspaceId; + this.__removeFromStudyList(studyData["uuid"]); + }) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); } - }; - osparc.data.Resources.fetch("studies", "moveToWorkspace", params) - .then(() => { - this.__removeFromStudyList(studyData["uuid"]); - }) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); + }, this); }, this); moveStudyToWorkspace.addListener("cancel", () => win.close()); }, this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js index 2b49a333b8a..dd7b80e5044 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js @@ -183,29 +183,12 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { const editButton = new qx.ui.menu.Button(this.tr("Edit..."), "@FontAwesome5Solid/pencil-alt/12"); editButton.addListener("execute", () => { const workspace = this.getWorkspace(); - const newWorkspace = false; - const workspaceEditor = new osparc.editor.WorkspaceEditor(newWorkspace).set({ - label: workspace.getName(), - description: workspace.getDescription(), - thumbnail: workspace.getThumbnail(), - }); + const workspaceEditor = new osparc.editor.WorkspaceEditor(workspace); const title = this.tr("Edit Workspace"); const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 200); - workspaceEditor.addListener("updateWorkspace", () => { - const newName = workspaceEditor.getLabel(); - const newDescription = workspaceEditor.getDescription(); - const newThumbnail = workspaceEditor.getThumbnail(); - const updateData = { - "name": newName, - "description": newDescription, - "thumbnail": newThumbnail, - }; - osparc.store.Workspaces.putWorkspace(this.getWorkspaceId(), updateData) - .then(() => { - this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId()); - }) - .catch(err => console.error(err)); + workspaceEditor.addListener("workspaceUpdated", () => { win.close(); + this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId()); }); workspaceEditor.addListener("cancel", () => win.close()); }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js index b06dd548529..4bdaf22a465 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js @@ -50,27 +50,19 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonNew", { members: { __itemSelected: function(newVal) { if (newVal) { - const workspaceEditor = new osparc.editor.WorkspaceEditor(true); + const workspaceCreator = new osparc.editor.WorkspaceEditor(); const title = this.tr("New Workspace"); - const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 200); - workspaceEditor.addListener("createWorkspace", () => { - const newWorkspaceData = { - name: workspaceEditor.getLabel(), - description: workspaceEditor.getDescription(), - thumbnail: workspaceEditor.getThumbnail(), - }; - osparc.store.Workspaces.postWorkspace(newWorkspaceData) - .then(newWorkspace => { - this.fireDataEvent("createWorkspace"); - const permissionsView = new osparc.share.CollaboratorsWorkspace(newWorkspace); - const title2 = qx.locale.Manager.tr("Share Workspace"); - osparc.ui.window.Window.popUpInWindow(permissionsView, title2, 500, 500); - permissionsView.addListener("updateAccessRights", () => this.fireDataEvent("updateWorkspace", newWorkspace.getWorkspaceId()), this); - }) - .catch(console.error) - .finally(() => win.close()); + const win = osparc.ui.window.Window.popUpInWindow(workspaceCreator, title, 300, 200); + workspaceCreator.addListener("workspaceCreated", e => { + win.close(); + const newWorkspace = e.getData(); + this.fireDataEvent("createWorkspace", newWorkspace.getWorkspaceId(), this); + const permissionsView = new osparc.share.CollaboratorsWorkspace(newWorkspace); + const title2 = qx.locale.Manager.tr("Share Workspace"); + osparc.ui.window.Window.popUpInWindow(permissionsView, title2, 500, 500); + permissionsView.addListener("updateAccessRights", () => this.fireDataEvent("updateWorkspace", newWorkspace.getWorkspaceId()), this); }); - workspaceEditor.addListener("cancel", () => win.close()); + workspaceCreator.addListener("cancel", () => win.close()); } this.setValue(false); } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceHeader.js new file mode 100644 index 00000000000..9fb3d526d13 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceHeader.js @@ -0,0 +1,297 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * Widget used for displaying a Workspace information + * + */ + +qx.Class.define("osparc.dashboard.WorkspaceHeader", { + extend: qx.ui.core.Widget, + + construct: function() { + this.base(arguments); + + this._setLayout(new qx.ui.layout.HBox(10)); + + this.set({ + minHeight: this.self().HEIGHT, + maxHeight: this.self().HEIGHT, + height: this.self().HEIGHT, + alignY: "middle", + }); + + this.__spacers = []; + }, + + events: { + "workspaceUpdated": "qx.event.type.Data", + "deleteWorkspaceRequested": "qx.event.type.Data" + }, + + properties: { + currentWorkspaceId: { + check: "Number", + nullable: true, + init: null, + event: "changeCurrentWorkspaceId", + apply: "__buildLayout" + }, + + accessRights: { + check: "Object", + nullable: false, + init: {}, + event: "changeAccessRights", + apply: "__applyAccessRights" + }, + + myAccessRights: { + check: "Object", + nullable: false, + init: {}, + event: "changeMyAccessRights", + apply: "__applyMyAccessRights" + } + }, + + statics: { + HEIGHT: 36 + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "icon": + control = new osparc.ui.basic.Thumbnail(null, this.self().HEIGHT, this.self().HEIGHT); + control.getChildControl("image").set({ + anonymous: true, + alignY: "middle", + alignX: "center", + allowGrowX: true, + allowGrowY: true + }); + control.getContentElement().setStyles({ + "border-radius": "4px" + }); + this._add(control); + break; + case "title": + control = new qx.ui.basic.Label().set({ + font: "text-16", + alignY: "middle", + }); + this._add(control); + break; + case "edit-button": + control = new qx.ui.form.MenuButton().set({ + appearance: "form-button-outlined", + backgroundColor: "transparent", + padding: [0, 8], + maxWidth: 22, + maxHeight: 22, + icon: "@FontAwesome5Solid/ellipsis-v/8", + focusable: false, + alignY: "middle", + }); + // make it circular + control.getContentElement().setStyles({ + "border-radius": `${22 / 2}px` + }); + this._add(control); + break; + case "share-layout": + this.__addSpacer(); + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + alignY: "middle" + })); + this._add(control); + break; + case "share-icon": { + control = new qx.ui.basic.Image().set({ + alignY: "middle", + allowGrowX: false, + allowShrinkX: false + }); + const layout = this.getChildControl("share-layout"); + layout.addAt(control, 0); + break; + } + case "share-text": { + control = new qx.ui.basic.Label().set({ + font: "text-14" + }); + const layout = this.getChildControl("share-layout"); + layout.addAt(control, 1); + break; + } + case "role-layout": + this.__addSpacer(); + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5).set({ + alignY: "middle" + })); + this._add(control); + break; + case "role-text": { + control = new qx.ui.basic.Label().set({ + font: "text-14" + }); + const layout = this.getChildControl("role-layout"); + layout.addAt(control, 0); + break; + } + case "role-icon": { + control = osparc.data.Roles.createRolesWorkspaceInfo(false); + const layout = this.getChildControl("role-layout"); + layout.addAt(control, 1); + break; + } + } + + return control || this.base(arguments, id); + }, + + __buildLayout: function(workspaceId) { + this.getChildControl("icon"); + const title = this.getChildControl("title"); + this.getChildControl("edit-button").exclude(); + + const workspace = osparc.store.Workspaces.getWorkspace(workspaceId); + if (workspaceId === -1) { + this.__setIcon(osparc.store.Workspaces.iconPath(32)); + title.setValue(this.tr("Shared Workspaces")); + this.resetAccessRights(); + this.resetMyAccessRights(); + } else if (workspace) { + const thumbnail = workspace.getThumbnail(); + this.__setIcon(thumbnail ? thumbnail : osparc.store.Workspaces.iconPath(32)); + workspace.bind("name", title, "value"); + workspace.bind("accessRights", this, "accessRights"); + workspace.bind("myAccessRights", this, "myAccessRights"); + } else { + this.__setIcon("@FontAwesome5Solid/home/30"); + title.setValue(this.tr("My Workspace")); + this.resetAccessRights(); + this.resetMyAccessRights(); + } + }, + + __addSpacer: function() { + const spacer = new qx.ui.basic.Label("-").set({ + font: "text-16", + alignY: "middle", + }); + this.__spacers.push(spacer); + this._add(spacer); + }, + + __setIcon: function(source) { + const icon = this.getChildControl("icon"); + const image = icon.getChildControl("image"); + image.resetSource(); + icon.getContentElement().setStyles({ + "background-image": "none" + }); + + if (source.includes("@")) { + image.set({ + source + }); + } else { + icon.getContentElement().setStyles({ + "background-image": `url(${source})`, + "background-repeat": "no-repeat", + "background-size": "cover", + "background-position": "center center", + "background-origin": "border-box" + }); + } + }, + + __showSpacers: function(show) { + this.__spacers.forEach(spacer => spacer.setVisibility(show ? "visible" : "excluded")); + }, + + __applyAccessRights: function(accessRights) { + const shareIcon = this.getChildControl("share-icon"); + const shareText = this.getChildControl("share-text"); + if (accessRights && Object.keys(accessRights).length) { + osparc.dashboard.CardBase.populateShareIcon(shareIcon, accessRights); + shareText.setValue(Object.keys(accessRights).length + " members"); + shareIcon.show(); + shareText.show(); + this.__showSpacers(true); + } else { + shareIcon.exclude(); + shareText.exclude(); + this.__showSpacers(false); + } + }, + + __applyMyAccessRights: function(value) { + const editButton = this.getChildControl("edit-button"); + const roleText = this.getChildControl("role-text"); + const roleIcon = this.getChildControl("role-icon"); + if (value && Object.keys(value).length) { + editButton.show(); + const menu = new qx.ui.menu.Menu().set({ + position: "bottom-right" + }); + const edit = new qx.ui.menu.Button(this.tr("Edit..."), "@FontAwesome5Solid/pencil-alt/12"); + edit.addListener("execute", () => this.__editWorkspace(), this); + menu.add(edit); + const share = new qx.ui.menu.Button(this.tr("Share..."), "@FontAwesome5Solid/share-alt/12"); + share.addListener("execute", () => this.__openShareWith(), this); + menu.add(share); + editButton.setMenu(menu); + const val = value["read"] + value["write"] + value["delete"]; + roleText.setValue(osparc.data.Roles.STUDY[val].label); + roleText.show(); + roleIcon.show(); + this.__showSpacers(true); + } else { + editButton.exclude(); + roleText.exclude(); + roleIcon.exclude(); + this.__showSpacers(false); + } + }, + + __editWorkspace: function() { + const workspace = osparc.store.Workspaces.getWorkspace(this.getCurrentWorkspaceId()); + const permissionsView = new osparc.editor.WorkspaceEditor(workspace); + const title = this.tr("Edit Workspace"); + const win = osparc.ui.window.Window.popUpInWindow(permissionsView, title, 300, 200); + permissionsView.addListener("workspaceUpdated", () => { + win.close(); + this.__buildLayout(this.getCurrentWorkspaceId()); + }, this); + }, + + __openShareWith: function() { + const workspace = osparc.store.Workspaces.getWorkspace(this.getCurrentWorkspaceId()); + const permissionsView = new osparc.share.CollaboratorsWorkspace(workspace); + const title = this.tr("Share Workspace"); + const win = osparc.ui.window.Window.popUpInWindow(permissionsView, title, 500, 400); + permissionsView.addListener("updateAccessRights", () => { + win.close(); + this.__applyAccessRights(workspace.getAccessRights()); + }, this); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js deleted file mode 100644 index f6959bb446b..00000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js +++ /dev/null @@ -1,41 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2024 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Class.define("osparc.dashboard.WorkspaceTreeItem", { - extend: qx.ui.tree.VirtualTreeItem, - - members: { - _addWidgets: function() { - this.addSpacer(); - // this.addOpenButton(); - const openButton = this.getChildControl("open"); - openButton.addListener("changeOpen", () => { - console.log("changeOpen", this); - }, this); - openButton.addListener("changeVisibility", e => { - // console.log("changeVisibility", this.getLabel() + e.getData(), this); - openButton.show(); - }, this); - this._add(openButton); - this.addIcon(); - const label = this.getChildControl("label"); - this._add(label, { - flex: 1 - }); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js index 0c0c2ce078e..5c853d273be 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js @@ -19,9 +19,14 @@ qx.Class.define("osparc.dashboard.WorkspacesTree", { extend: qx.ui.tree.VirtualTree, construct: function() { - const rootLabel = "Root"; - const rootWorkspace = this.self().createNewEntry(rootLabel, null); - const root = qx.data.marshal.Json.createModel(rootWorkspace, true); + const rootData = { + label: "Workspaces", + icon: "default", + workspaceId: -1, + children: [], + loaded: true, + }; + const root = qx.data.marshal.Json.createModel(rootData, true); this.__fetchChildren(root); this.base(arguments, root, "label", "children"); @@ -32,7 +37,6 @@ qx.Class.define("osparc.dashboard.WorkspacesTree", { font: "text-14", showLeafs: true, paddingLeft: -10, - hideRoot: true }); this.__initTree(); @@ -42,48 +46,12 @@ qx.Class.define("osparc.dashboard.WorkspacesTree", { "selectionChanged": "qx.event.type.Event" // tap }, - statics: { - createNewEntry: function(label, workspaceId) { - return { - label, - workspaceId, - children: [ - this.self().getLoadingData() - ], - loaded: false, - }; - }, - - getLoadingData: function() { - return { - workspaceId: -1, - label: "Loading...", - children: [], - icon: "@FontAwesome5Solid/circle-notch/12", - loaded: false, - }; - }, - - addLoadingChild: function(parentModel) { - const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); - parentModel.getChildren().append(loadingModel); - }, - - removeLoadingChild: function(parent) { - for (let i = parent.getChildren().getLength() - 1; i >= 0; i--) { - if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { - parent.getChildren().splice(i, 1); - } - } - } - }, - members: { __currentWorkspaceId:null, __initTree: function() { this.setDelegate({ - createItem: () => new osparc.dashboard.WorkspaceTreeItem(), + createItem: () => new qx.ui.tree.VirtualTreeItem(), bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); c.bindProperty("workspaceId", "model", null, item, id); @@ -103,22 +71,41 @@ qx.Class.define("osparc.dashboard.WorkspacesTree", { return aLabel - bLabel; } }); + + this.setIconPath("icon"); + this.setIconOptions({ + converter(value) { + if (value === "shared") { + return osparc.store.Workspaces.iconPath(16); + } + return "@FontAwesome5Solid/folder/14"; + }, + }); }, - __fetchChildren: function(parentModel) { - parentModel.setLoaded(true); + __fetchChildren: function(parent) { + parent.setLoaded(true); - const myWorkspaceData = this.self().createNewEntry("My Workspace", null); + const myWorkspaceData = { + label: "My Workspace", + icon: "default", + workspaceId: null, + loaded: true, + }; const myWorkspaceModel = qx.data.marshal.Json.createModel(myWorkspaceData, true); - parentModel.getChildren().append(myWorkspaceModel); + parent.getChildren().append(myWorkspaceModel); osparc.store.Workspaces.fetchWorkspaces() .then(workspaces => { - this.self().removeLoadingChild(parentModel); workspaces.forEach(workspace => { - const workspaceData = this.self().createNewEntry(workspace.getName(), workspace.getWorkspaceId()); + const workspaceData = { + label: workspace.getName(), + icon: "shared", + workspaceId: workspace.getWorkspaceId(), + loaded: true, + }; const workspaceModel = qx.data.marshal.Json.createModel(workspaceData, true); - parentModel.getChildren().append(workspaceModel); + parent.getChildren().append(workspaceModel); }); }) .catch(console.error); diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index d9d2b517212..ab670eee35f 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -29,6 +29,7 @@ qx.Class.define("osparc.data.model.Folder", { this.base(arguments); this.set({ + workspaceId: folderData.workspaceId, folderId: folderData.folderId, parentId: folderData.parentFolderId, name: folderData.name, @@ -40,11 +41,18 @@ qx.Class.define("osparc.data.model.Folder", { }, properties: { + workspaceId: { + check: "Number", + nullable: true, + init: null, + event: "changeWorkspaceId" + }, + folderId: { check: "Number", nullable: false, init: null, - event: "changeId" + event: "changeFolderId" }, parentId: { diff --git a/services/static-webserver/client/source/class/osparc/data/model/Study.js b/services/static-webserver/client/source/class/osparc/data/model/Study.js index 0df98435304..4b7c879bb53 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Study.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Study.js @@ -44,6 +44,7 @@ qx.Class.define("osparc.data.model.Study", { this.set({ uuid: studyData.uuid || this.getUuid(), + workspaceId: studyData.workspaceId || null, name: studyData.name || this.getName(), description: studyData.description || this.getDescription(), thumbnail: studyData.thumbnail || this.getThumbnail(), @@ -78,6 +79,13 @@ qx.Class.define("osparc.data.model.Study", { init: "" }, + workspaceId: { + check: "Number", + init: true, + nullable: true, + event: "changeWorkspaceId" + }, + name: { check: "String", nullable: false, diff --git a/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js b/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js index 1948026e352..46c2626fc1a 100644 --- a/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js @@ -56,7 +56,7 @@ qx.Class.define("osparc.editor.FolderEditor", { font: "text-14", backgroundColor: "background-main", placeholder: this.tr("Title"), - height: 35 + height: 27 }); this.bind("label", control, "value"); control.bind("value", this, "label"); diff --git a/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js index 3a654307006..465b074a864 100644 --- a/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js @@ -18,7 +18,7 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { extend: qx.ui.core.Widget, - construct: function(newWorkspace = true) { + construct: function(workspace) { this.base(arguments); this._setLayout(new qx.ui.layout.VBox(8)); @@ -29,7 +29,15 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { manager.add(title); this.getChildControl("description"); this.getChildControl("thumbnail"); - newWorkspace ? this.getChildControl("create") : this.getChildControl("save"); + workspace ? this.getChildControl("save") : this.getChildControl("create"); + if (workspace) { + this.__workspaceId = workspace.getWorkspaceId(); + this.set({ + label: workspace.getName(), + description: workspace.getDescription(), + thumbnail: workspace.getThumbnail(), + }); + } this.addListener("appear", this.__onAppear, this); }, @@ -55,23 +63,17 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { nullable: false, event: "changeThumbnail" }, - - accessRights: { - check: "Object", - init: {}, - nullable: false, - event: "changeAccessRights", - apply: "applyAccessRights" - } }, events: { - "createWorkspace": "qx.event.type.Event", - "updateWorkspace": "qx.event.type.Event", + "workspaceCreated": "qx.event.type.Data", + "workspaceUpdated": "qx.event.type.Event", "cancel": "qx.event.type.Event" }, members: { + __workspaceId: null, + _createChildControlImpl: function(id) { let control; switch (id) { @@ -80,6 +82,7 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { font: "text-14", backgroundColor: "background-main", placeholder: this.tr("Title"), + minHeight: 27 }); this.bind("label", control, "value"); control.bind("value", this, "label"); @@ -115,8 +118,7 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { }); control.addListener("execute", () => { if (this.__validator.validate()) { - control.setFetching(true); - this.fireEvent("createWorkspace"); + this.__createWorkspace(control); } }, this); buttons.addAt(control, 1); @@ -129,8 +131,7 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { }); control.addListener("execute", () => { if (this.__validator.validate()) { - control.setFetching(true); - this.fireEvent("updateWorkspace"); + this.__editWorkspace(control); } }, this); buttons.addAt(control, 1); @@ -153,6 +154,32 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { return control || this.base(arguments, id); }, + __createWorkspace: function(createButton) { + createButton.setFetching(true); + const newWorkspaceData = { + name: this.getLabel(), + description: this.getDescription(), + thumbnail: this.getThumbnail(), + }; + osparc.store.Workspaces.postWorkspace(newWorkspaceData) + .then(newWorkspace => this.fireDataEvent("workspaceCreated", newWorkspace)) + .catch(console.error) + .finally(() => createButton.setFetching(false)); + }, + + __editWorkspace: function(editButton) { + editButton.setFetching(true); + const updateData = { + name: this.getLabel(), + description: this.getDescription(), + thumbnail: this.getThumbnail(), + }; + osparc.store.Workspaces.putWorkspace(this.__workspaceId, updateData) + .then(() => this.fireEvent("workspaceUpdated")) + .catch(console.error) + .finally(() => editButton.setFetching(false)); + }, + __onAppear: function() { const title = this.getChildControl("title"); title.focus(); diff --git a/services/static-webserver/client/source/class/osparc/study/Utils.js b/services/static-webserver/client/source/class/osparc/study/Utils.js index b88ed539257..e9b49ec1b9f 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -220,7 +220,7 @@ qx.Class.define("osparc.study.Utils", { if ("task_progress" in updateData && loadingPage) { const progress = updateData["task_progress"]; const message = progress["message"]; - const percent = progress["percent"]; + const percent = progress["percent"] ? progress["percent"].toFixed(3) : progress["percent"]; progressSequence.setOverallProgress(percent); const existingTask = progressSequence.getTask(message); if (existingTask) { diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js index 4f2928f4cf9..c648b18e8d6 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js @@ -2014,7 +2014,7 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { if (this.__isDraggingLink && "dragData" in this.__isDraggingLink) { const pos = this.__pointerEventToWorkbenchPos(e, false); const service = qx.data.marshal.Json.createModel(osparc.service.Utils.getFilePicker()); - const nodeUI = this.__addNode(service, pos); + const nodeUI = await this.__addNode(service, pos); if (nodeUI) { const node = nodeUI.getNode(); const data = this.__isDraggingLink["dragData"];