From 54e81746879afed7ce4f9de810b828e418012587 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:19:42 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20[Frontend]=20Move=20multiple=20s?= =?UTF-8?q?tudies=20at=20once=20(#6457)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/class/osparc/dashboard/CardBase.js | 10 +- .../osparc/dashboard/FolderButtonItem.js | 13 +- .../osparc/dashboard/GridButtonLoadMore.js | 11 +- .../osparc/dashboard/ListButtonLoadMore.js | 10 +- .../class/osparc/dashboard/MoveResourceTo.js | 125 +++++++ .../osparc/dashboard/MoveResourceToFolder.js | 94 ----- .../dashboard/MoveResourceToWorkspace.js | 98 ------ .../osparc/dashboard/ResourceBrowserBase.js | 11 +- .../dashboard/ResourceContainerManager.js | 8 +- .../class/osparc/dashboard/StudyBrowser.js | 323 +++++++++++------- .../dashboard/WorkspacesAndFoldersTree.js | 107 +++--- .../class/osparc/editor/FolderEditor.js | 11 +- .../client/source/class/osparc/study/Utils.js | 7 +- 13 files changed, 421 insertions(+), 407 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js delete mode 100644 services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js delete mode 100644 services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 3a065867cd6..8d59dee3728 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -784,13 +784,9 @@ qx.Class.define("osparc.dashboard.CardBase", { if (billingSettingsButton) { billingSettingsButton.setEnabled(osparc.study.Utils.canShowBillingOptions(resourceData)); } - const moveToFolderButton = menuButtons.find(menuBtn => "moveToFolderButton" in menuBtn); - if (moveToFolderButton) { - moveToFolderButton.setEnabled(osparc.study.Utils.canMoveToFolder(resourceData)); - } - const moveToWorkspaceButton = menuButtons.find(menuBtn => "moveToWorkspaceButton" in menuBtn); - if (moveToWorkspaceButton) { - moveToWorkspaceButton.setEnabled(osparc.study.Utils.canMoveToWorkspace(resourceData)); + const moveToButton = menuButtons.find(menuBtn => "moveToButton" in menuBtn); + if (moveToButton) { + moveToButton.setEnabled(osparc.study.Utils.canMoveTo(resourceData)); } const deleteButton = menuButtons.find(menuBtn => "deleteButton" in menuBtn); if (deleteButton) { 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 79847e46334..355b1db7c87 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -45,8 +45,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { events: { "folderSelected": "qx.event.type.Data", "folderUpdated": "qx.event.type.Data", - "moveFolderToFolderRequested": "qx.event.type.Data", - "moveFolderToWorkspaceRequested": "qx.event.type.Data", + "moveFolderToRequested": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data" }, @@ -191,13 +190,9 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { editButton.addListener("execute", () => this.__editFolder(), this); menu.add(editButton); - const moveToFolderButton = new qx.ui.menu.Button(this.tr("Move to Folder..."), "@FontAwesome5Solid/folder/12"); - moveToFolderButton.addListener("execute", () => this.fireDataEvent("moveFolderToFolderRequested", this.getFolderId()), this); - menu.add(moveToFolderButton); - - 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); + const moveToButton = new qx.ui.menu.Button(this.tr("Move to..."), "@FontAwesome5Solid/folder/12"); + moveToButton.addListener("execute", () => this.fireDataEvent("moveFolderToRequested", this.getFolderId()), this); + menu.add(moveToButton); menu.addSeparator(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js index e97a048df0d..a10d57dcaa2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js @@ -29,22 +29,21 @@ qx.Class.define("osparc.dashboard.GridButtonLoadMore", { this._applyFetching(false); + // A minimalistic spinning wheel + this.getChildControl("header").exclude(); this.getChildControl("footer").exclude(); + this.set({ + backgroundColor: "transparent" + }); }, members: { _applyFetching: function(value) { - const title = this.getChildControl("title"); - const desc = this.getChildControl("subtitle-text"); this.setIcon(osparc.dashboard.CardBase.LOADING_ICON); if (value) { - title.setValue(this.tr("Loading...")); - desc.setValue(""); this.getChildControl("icon").getChildControl("image").getContentElement() .addClass("rotate"); } else { - title.setValue(this.tr("Load More")); - desc.setValue(this.tr("Click to load more").toString()); this.getChildControl("icon").getChildControl("image").getContentElement() .removeClass("rotate"); } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js index b1df53a2aa5..cbf818c8cdc 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js @@ -30,19 +30,19 @@ qx.Class.define("osparc.dashboard.ListButtonLoadMore", { this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.LOADER); this._applyFetching(false); + + this.set({ + backgroundColor: "transparent" + }); }, members: { _applyFetching: function(value) { - const title = this.getChildControl("title"); + this.setIcon(osparc.dashboard.CardBase.LOADING_ICON); if (value) { - title.setValue(this.tr("Loading...")); - this.setIcon(osparc.dashboard.CardBase.LOADING_ICON); this.getChildControl("icon").getChildControl("image").getContentElement() .addClass("rotate"); } else { - title.setValue(this.tr("Load More")); - this.setIcon("@FontAwesome5Solid/paw/"); this.getChildControl("icon").getChildControl("image").getContentElement() .removeClass("rotate"); } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js new file mode 100644 index 00000000000..11a744ba9ea --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js @@ -0,0 +1,125 @@ +/* ************************************************************************ + + 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.MoveResourceTo", { + extend: qx.ui.core.Widget, + + construct: function(currentWorkspaceId, currentFolderId) { + this.base(arguments); + + this.__currentWorkspaceId = currentWorkspaceId; + this.__currentFolderId = currentFolderId; + + this._setLayout(new qx.ui.layout.VBox(10)); + + this.getChildControl("current-location"); + + const workspacesAndFoldersTree = this.getChildControl("workspaces-and-folders-tree"); + this.getChildControl("cancel-btn"); + const moveButton = this.getChildControl("move-btn"); + + moveButton.setEnabled(false); + workspacesAndFoldersTree.getSelection().addListener("change", () => { + const selection = workspacesAndFoldersTree.getSelection(); + if (selection.getLength() > 0) { + const item = selection.getItem(0); + this.__selectedWorkspaceId = item.getWorkspaceId(); + this.__selectedFolderId = item.getFolderId(); + moveButton.setEnabled(this.__currentWorkspaceId !== this.__selectedWorkspaceId || this.__currentFolderId !== this.__selectedFolderId); + } + }, this); + moveButton.addListener("execute", () => { + this.fireDataEvent("moveTo", { + workspaceId: this.__selectedWorkspaceId, + folderId: this.__selectedFolderId, + }); + }, this); + }, + + events: { + "cancel": "qx.event.type.Event", + "moveTo": "qx.event.type.Data" + }, + + members: { + __currentWorkspaceId: null, + __currentFolderId: null, + __selectedWorkspaceId: null, + __selectedFolderId: null, + + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "current-location": { + control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + const intro = new qx.ui.basic.Label(this.tr("Current location")); + control.add(intro); + const workspace = osparc.store.Workspaces.getInstance().getWorkspace(this.__currentWorkspaceId); + const workspaceText = workspace ? workspace.getName() : "My Workspace"; + const workspaceLabel = new qx.ui.basic.Label(this.tr("- Workspace: ") + workspaceText); + control.add(workspaceLabel); + const folder = osparc.store.Folders.getInstance().getFolder(this.__currentFolderId); + if (folder) { + const folderText = folder.getName(); + const folderLabel = new qx.ui.basic.Label(this.tr("- Folder: ") + folderText); + control.add(folderLabel); + } + this._add(control); + break; + } + case "workspaces-and-folders-tree": + control = new osparc.dashboard.WorkspacesAndFoldersTree(); + this._add(control, { + flex: 1 + }); + break; + case "buttons-layout": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + alignX: "right" + })); + this._add(control); + break; + case "cancel-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Cancel")).set({ + appearance: "form-button-text" + }); + control.addListener("execute", () => this.fireEvent("cancel"), this); + buttons.add(control); + break; + } + case "move-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Move")).set({ + appearance: "form-button" + }); + buttons.add(control); + break; + } + } + return control || this.base(arguments, id); + }, + + __getUpstreamFolders: function(childFolder, folderIds = []) { + if (childFolder) { + folderIds.unshift(childFolder.getFolderId()); + const parentFolder = osparc.store.Folders.getInstance().getFolder(childFolder.getParentFolderId()); + this.__getUpstreamFolders(parentFolder, folderIds); + } + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js deleted file mode 100644 index 06ebb83cfeb..00000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js +++ /dev/null @@ -1,94 +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.MoveResourceToFolder", { - extend: qx.ui.core.Widget, - - construct: function(currentFolderId, currentWorkspaceId) { - this.base(arguments); - - this.__currentFolderId = currentFolderId; - this.__currentWorkspaceId = currentWorkspaceId; - - this._setLayout(new qx.ui.layout.VBox(10)); - - this.getChildControl("current-folder"); - const foldersTree = this.getChildControl("folders-tree"); - this.getChildControl("cancel-btn"); - const moveButton = this.getChildControl("move-btn"); - - moveButton.setEnabled(false) - foldersTree.addListener("selectionChanged", e => { - const folderId = e.getData(); - moveButton.setEnabled(this.__currentFolderId !== folderId); - this.__selectedFolderId = folderId; - }, this); - moveButton.addListener("execute", () => { - this.fireDataEvent("moveToFolder", this.__selectedFolderId); - }, this); - }, - - events: { - "cancel": "qx.event.type.Event", - "moveToFolder": "qx.event.type.Data" - }, - - members: { - __currentFolderId: null, - - _createChildControlImpl: function(id) { - let control; - switch (id) { - case "current-folder": { - const folder = osparc.store.Folders.getInstance().getFolder(this.__currentFolderId); - const currentFolderName = folder ? folder.getName() : "Home"; - control = new qx.ui.basic.Label(this.tr("Current location: ") + currentFolderName); - this._add(control); - break; - } - case "folders-tree": - control = new osparc.dashboard.FoldersTree(this.__currentWorkspaceId); - this._add(control); - break; - case "buttons-layout": - control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ - alignX: "right" - })); - this._add(control); - break; - case "cancel-btn": { - const buttons = this.getChildControl("buttons-layout"); - control = new qx.ui.form.Button(this.tr("Cancel")).set({ - appearance: "form-button-text" - }); - control.addListener("execute", () => this.fireEvent("cancel"), this); - buttons.add(control); - break; - } - case "move-btn": { - const buttons = this.getChildControl("buttons-layout"); - control = new qx.ui.form.Button(this.tr("Move")).set({ - appearance: "form-button" - }); - buttons.add(control); - break; - } - } - return control || this.base(arguments, id); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js deleted file mode 100644 index 942b21cc8ca..00000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js +++ /dev/null @@ -1,98 +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.MoveResourceToWorkspace", { - extend: qx.ui.core.Widget, - - construct: function(currentWorkspaceId) { - this.base(arguments); - - this.__currentWorkspaceId = currentWorkspaceId; - - this._setLayout(new qx.ui.layout.VBox(10)); - - this.getChildControl("current-workspace"); - const workspacesTree = this.getChildControl("workspaces-tree"); - this.getChildControl("cancel-btn"); - const moveButton = this.getChildControl("move-btn"); - - moveButton.setEnabled(false) - workspacesTree.addListener("selectionChanged", e => { - const workspaceId = e.getData(); - if (this.__currentWorkspaceId !== workspaceId && workspaceId !== -1) { - moveButton.setEnabled(true); - this.__selectedWorkspaceId = workspaceId; - } else { - moveButton.setEnabled(false); - } - }, this); - moveButton.addListener("execute", () => { - this.fireDataEvent("moveToWorkspace", this.__selectedWorkspaceId); - }, this); - }, - - events: { - "cancel": "qx.event.type.Event", - "moveToWorkspace": "qx.event.type.Data" - }, - - members: { - __currentWorkspaceId: null, - __selectedWorkspaceId: null, - - _createChildControlImpl: function(id) { - let control; - switch (id) { - case "current-workspace": { - const workspace = osparc.store.Workspaces.getInstance().getWorkspace(this.__currentWorkspaceId); - const currentWorkspaceName = workspace ? workspace.getName() : "My Workspace"; - control = new qx.ui.basic.Label(this.tr("Current location: ") + currentWorkspaceName); - this._add(control); - break; - } - case "workspaces-tree": - control = new osparc.dashboard.WorkspacesTree(); - this._add(control); - break; - case "buttons-layout": - control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ - alignX: "right" - })); - this._add(control); - break; - case "cancel-btn": { - const buttons = this.getChildControl("buttons-layout"); - control = new qx.ui.form.Button(this.tr("Cancel")).set({ - appearance: "form-button-text" - }); - control.addListener("execute", () => this.fireEvent("cancel"), this); - buttons.add(control); - break; - } - case "move-btn": { - const buttons = this.getChildControl("buttons-layout"); - control = new qx.ui.form.Button(this.tr("Move")).set({ - appearance: "form-button" - }); - buttons.add(control); - break; - } - } - return control || this.base(arguments, id); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 4535895047a..97bd5f0f318 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -81,7 +81,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, statics: { - PAGINATED_STUDIES: 10, + PAGINATED_STUDIES: 5, MIN_GRID_CARDS_PER_ROW: 3, SIDE_SPACER_WIDTH: 200, @@ -264,8 +264,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("tagClicked", e => this._searchBarFilter.addTagActiveFilter(e.getData())); resourcesContainer.addListener("emptyStudyClicked", e => this._deleteResourceRequested(e.getData())); resourcesContainer.addListener("folderUpdated", e => this._folderUpdated(e.getData())); - resourcesContainer.addListener("moveFolderToFolderRequested", e => this._moveFolderToFolderRequested(e.getData())); - resourcesContainer.addListener("moveFolderToWorkspaceRequested", e => this._moveFolderToWorkspaceRequested(e.getData())); + resourcesContainer.addListener("moveFolderToRequested", e => this._moveFolderToRequested(e.getData())); resourcesContainer.addListener("deleteFolderRequested", e => this._deleteFolderRequested(e.getData())); resourcesContainer.addListener("folderSelected", e => { const folderId = e.getData(); @@ -502,11 +501,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, - _moveFolderToFolderRequested: function(folderId) { - throw new Error("Abstract method called!"); - }, - - _moveFolderToWorkspaceRequested: function(folderId) { + _moveFolderToRequested: function(folderId) { throw new Error("Abstract method called!"); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index dcc83b6ff91..e3a2d8e9cf0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -85,8 +85,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "changeVisibility": "qx.event.type.Data", "folderSelected": "qx.event.type.Data", "folderUpdated": "qx.event.type.Data", - "moveFolderToFolderRequested": "qx.event.type.Data", - "moveFolderToWorkspaceRequested": "qx.event.type.Data", + "moveFolderToRequested": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data", "workspaceSelected": "qx.event.type.Data", "workspaceUpdated": "qx.event.type.Data", @@ -209,7 +208,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { }, resetSelection: function() { - if (this.getGroupBy() === null) { + if (this.getGroupBy() === null && this.__nonGroupedContainer) { this.__nonGroupedContainer.resetSelection(); } }, @@ -397,8 +396,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { [ "folderSelected", "folderUpdated", - "moveFolderToFolderRequested", - "moveFolderToWorkspaceRequested", + "moveFolderToRequested", "deleteFolderRequested", ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); return card; 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 a17e959086c..1249aaa5a9b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -453,6 +453,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _changeContext: function(workspaceId, folderId) { if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { + this.resetSelection(); + this.setMultiSelection(false); this.set({ currentWorkspaceId: workspaceId, currentFolderId: folderId, @@ -513,29 +515,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadFolders(); }, - _moveFolderToFolderRequested: function(folderId) { - const moveFolderToFolder = new osparc.dashboard.MoveResourceToFolder(this.getCurrentFolderId(), this.getCurrentWorkspaceId()); - const title = "Move to Folder"; - const win = osparc.ui.window.Window.popUpInWindow(moveFolderToFolder, title, 350, 280); - moveFolderToFolder.addListener("moveToFolder", e => { - win.close(); - const folder = osparc.store.Folders.getInstance().getFolder(folderId); - const destFolderId = e.getData(); - const updatedData = { - name: folder.getName(), - parentFolderId: destFolderId, - }; - osparc.store.Folders.getInstance().putFolder(folderId, updatedData) - .then(() => { - folder.setParentFolderId(destFolderId); - this.__reloadFolders() - }) - .catch(err => console.error(err)); - }); - }, - __showMoveToWorkspaceWarningMessage: function() { - const msg = this.tr("The permissions will be taken from the new workspace?"); + 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"), }); @@ -543,40 +524,73 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return win; }, - _moveFolderToWorkspaceRequested: function(folderId) { - const folderToWorkspaceRequested = false; - if (!folderToWorkspaceRequested) { - const msg = this.tr("Coming soon"); - osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); - return; - } - const moveFolderToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentWorkspaceId()); - const title = "Move to Workspace"; - const win = osparc.ui.window.Window.popUpInWindow(moveFolderToWorkspace, title, 350, 280); - moveFolderToWorkspace.addListener("moveToWorkspace", e => { + _moveFolderToRequested: function(folderId) { + const currentWorkspaceId = this.getCurrentWorkspaceId(); + const currentFolderId = this.getCurrentWorkspaceId(); + const moveFolderTo = new osparc.dashboard.MoveResourceTo(currentWorkspaceId, currentFolderId); + const title = this.tr("Move to..."); + const win = osparc.ui.window.Window.popUpInWindow(moveFolderTo, title, 400, 400); + moveFolderTo.addListener("moveTo", e => { win.close(); - const destWorkspaceId = e.getData(); - 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)); - } - }, this); - }, this); + const data = e.getData(); + const destWorkspaceId = data["workspaceId"]; + const destFolderId = data["folderId"]; + if (destWorkspaceId !== currentWorkspaceId) { + const msg = this.tr("Coming soon"); + osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); + return; + } + const moveFolder = () => { + Promise.all([ + this.__moveFolderToWorkspace(folderId, destWorkspaceId), + this.__moveFolderToFolder(folderId, destFolderId), + ]) + .then(() => this.__reloadFolders()) + .catch(err => console.error(err)); + } + if (destWorkspaceId === currentWorkspaceId) { + moveFolder(); + } else { + const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + moveFolder(); + } + }, this); + } + }); + }, + + __moveFolderToWorkspace: function(folderId, destWorkspaceId) { + const folder = osparc.store.Folders.getInstance().getFolder(folderId); + if (folder.getWorkspaceId() === destWorkspaceId) { + // resolve right away + return new Promise(resolve => resolve()); + } + const params = { + url: { + folderId, + workspaceId: destWorkspaceId, + } + }; + return osparc.data.Resources.fetch("folders", "moveToWorkspace", params) + .then(() => folder.setWorkspaceId(destWorkspaceId)) + .catch(err => console.error(err)); + }, + + __moveFolderToFolder: function(folderId, destFolderId) { + if (folderId === destFolderId) { + // resolve right away + return new Promise(resolve => resolve()); + } + const folder = osparc.store.Folders.getInstance().getFolder(folderId); + const updatedData = { + name: folder.getName(), + parentFolderId: destFolderId, + }; + return osparc.store.Folders.getInstance().putFolder(folderId, updatedData) + .then(() => folder.setParentFolderId(destFolderId)) + .catch(err => console.error(err)); }, _deleteFolderRequested: function(folderId) { @@ -895,6 +909,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const selectStudiesButton = this.__createSelectButton(); this._toolbar.add(selectStudiesButton); + const studiesMoveButton = this.__createMoveStudiesButton(false); + this._toolbar.add(studiesMoveButton); + const studiesDeleteButton = this.__createDeleteButton(false); this._toolbar.add(studiesDeleteButton); @@ -925,6 +942,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesContainer.addListener("changeSelection", e => { const selection = e.getData(); + + studiesMoveButton.set({ + visibility: selection.length ? "visible" : "excluded", + label: selection.length > 1 ? this.tr("Move selected")+" ("+selection.length+")" : this.tr("Move") + }); + studiesDeleteButton.set({ visibility: selection.length ? "visible" : "excluded", label: selection.length > 1 ? this.tr("Delete selected")+" ("+selection.length+")" : this.tr("Delete") @@ -1014,6 +1037,52 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return importButton; }, + __createMoveStudiesButton: function() { + const moveStudiesButton = new qx.ui.form.Button(this.tr("Move to")).set({ + appearance: "form-button-outlined", + visibility: "excluded", + }); + moveStudiesButton.addListener("execute", () => { + const currentWorkspaceId = this.getCurrentWorkspaceId(); + const currentFolderId = this.getCurrentFolderId(); + const moveStudyTo = new osparc.dashboard.MoveResourceTo(currentWorkspaceId, currentFolderId); + const title = this.tr("Move to..."); + const win = osparc.ui.window.Window.popUpInWindow(moveStudyTo, title, 400, 400); + moveStudyTo.addListener("moveTo", e => { + win.close(); + const data = e.getData(); + const destWorkspaceId = data["workspaceId"]; + const destFolderId = data["folderId"]; + const moveStudies = () => { + const selection = this._resourcesContainer.getSelection(); + selection.forEach(button => { + const studyData = button.getResourceData(); + Promise.all([ + this.__moveStudyToWorkspace(studyData, destWorkspaceId), + this.__moveStudyToFolder(studyData, destFolderId), + ]) + .then(() => this.__removeFromStudyList(studyData["uuid"])) + .catch(err => console.error(err)); + }); + this.resetSelection(); + this.setMultiSelection(false); + } + if (destWorkspaceId === currentWorkspaceId) { + moveStudies(); + } else { + const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + moveStudies(); + } + }, this); + } + }, this); + moveStudyTo.addListener("cancel", () => win.close()); + }, this); + return moveStudiesButton; + }, + __createDeleteButton: function() { const deleteButton = new qx.ui.form.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/14").set({ appearance: "danger-button", @@ -1231,14 +1300,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (writeAccess && osparc.utils.DisabledPlugins.isFoldersEnabled()) { menu.addSeparator(); - const moveToFolderButton = this.__getMoveStudyToFolderMenuButton(studyData); - if (moveToFolderButton) { - menu.add(moveToFolderButton); - } - - const moveToWorkspaceButton = this.__getMoveStudyToWorkspaceMenuButton(studyData); - if (moveToWorkspaceButton) { - menu.add(moveToWorkspaceButton); + const moveToButton = this.__getMoveStudyToMenuButton(studyData); + if (moveToButton) { + menu.add(moveToButton); } } @@ -1325,73 +1389,80 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return studyBillingSettingsButton; }, - __getMoveStudyToFolderMenuButton: function(studyData) { - const text = this.tr("Move to Folder..."); - const moveToFolderButton = new qx.ui.menu.Button(text, "@FontAwesome5Solid/folder/12"); - moveToFolderButton["moveToFolderButton"] = true; - moveToFolderButton.addListener("tap", () => { - const title = this.tr("Move") + " " + studyData["name"]; - const moveStudyToFolder = new osparc.dashboard.MoveResourceToFolder(this.getCurrentFolderId(), this.getCurrentWorkspaceId()); - const win = osparc.ui.window.Window.popUpInWindow(moveStudyToFolder, title, 350, 280); - moveStudyToFolder.addListener("moveToFolder", e => { + __getMoveStudyToMenuButton: function(studyData) { + const moveToButton = new qx.ui.menu.Button(this.tr("Move to..."), "@FontAwesome5Solid/folder/12"); + moveToButton["moveToButton"] = true; + moveToButton.addListener("tap", () => { + const currentWorkspaceId = this.getCurrentWorkspaceId(); + const currentFolderId = this.getCurrentWorkspaceId(); + const moveStudyTo = new osparc.dashboard.MoveResourceTo(currentWorkspaceId, currentFolderId); + const title = this.tr("Move to..."); + const win = osparc.ui.window.Window.popUpInWindow(moveStudyTo, title, 400, 400); + moveStudyTo.addListener("moveTo", e => { win.close(); - const destFolderId = e.getData(); - const params = { - url: { - studyId: studyData["uuid"], - folderId: destFolderId, - } + const data = e.getData(); + const destWorkspaceId = data["workspaceId"]; + const destFolderId = data["folderId"]; + const moveStudy = () => { + Promise.all([ + this.__moveStudyToWorkspace(studyData, destWorkspaceId), + this.__moveStudyToFolder(studyData, destFolderId), + ]) + .then(() => this.__removeFromStudyList(studyData["uuid"])) + .catch(err => console.error(err)); }; - osparc.data.Resources.fetch("studies", "moveToFolder", params) - .then(() => { - studyData["folderId"] = destFolderId; - this.__removeFromStudyList(studyData["uuid"]); - }) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); - }, this); - moveStudyToFolder.addListener("cancel", () => win.close()); - }, this); - return moveToFolderButton; - }, - - __getMoveStudyToWorkspaceMenuButton: function(studyData) { - const text = this.tr("Move to Workspace..."); - const moveToWorkspaceButton = new qx.ui.menu.Button(text, osparc.store.Workspaces.iconPath(14)); - moveToWorkspaceButton["moveToWorkspaceButton"] = true; - moveToWorkspaceButton.addListener("tap", () => { - const title = this.tr("Move") + " " + studyData["name"]; - 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 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"); - }); - } - }, this); + if (destWorkspaceId === currentWorkspaceId) { + moveStudy(); + } else { + const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + moveStudy(); + } + }, this); + } }, this); - moveStudyToWorkspace.addListener("cancel", () => win.close()); + moveStudyTo.addListener("cancel", () => win.close()); }, this); - return moveToWorkspaceButton; + return moveToButton; + }, + + __moveStudyToWorkspace: function(studyData, destWorkspaceId) { + if (studyData["workspaceId"] === destWorkspaceId) { + // resolve right away + return new Promise(resolve => resolve()); + } + const params = { + url: { + studyId: studyData["uuid"], + workspaceId: destWorkspaceId, + } + }; + return osparc.data.Resources.fetch("studies", "moveToWorkspace", params) + .then(() => studyData["workspaceId"] = destWorkspaceId) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); + }, + + __moveStudyToFolder: function(studyData, destFolderId) { + if (studyData["folderId"] === destFolderId) { + // resolve right away + return new Promise(resolve => resolve()); + } + const params = { + url: { + studyId: studyData["uuid"], + folderId: destFolderId, + } + }; + return osparc.data.Resources.fetch("studies", "moveToFolder", params) + .then(() => studyData["folderId"] = destFolderId) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); }, __getDuplicateMenuButton: function(studyData) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js index 557de2bef7f..9943abad432 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js @@ -36,7 +36,6 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { decorator: "no-border", font: "text-14", hideRoot: true, - // paddingLeft: -10, contentPadding: 0, padding: 0, }); @@ -89,7 +88,6 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { nullable: true, init: null, event: "changeCurrentWorkspaceId", - // apply: "__applyCurrentWorkspaceId" }, currentFolderId: { @@ -97,7 +95,6 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { nullable: true, init: null, event: "changeCurrentFolderId", - // apply: "__resetAndReloadAll" }, }, @@ -111,11 +108,18 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); c.bindProperty("", "open", { - converter(value, model, source, target) { + converter(value, _model, _source, target) { const isOpen = target.isOpen(); - if (isOpen && !value.getLoaded()) { + if (isOpen) { // eslint-disable-next-line no-underscore-dangle - that.__populateFolder(value, value.getWorkspaceId(), value.getFolderId()); + that.__populateFolder(value, value.getWorkspaceId(), value.getFolderId()) + .then(folderModels => { + // load next level too + folderModels.forEach(folderModel => { + // eslint-disable-next-line no-underscore-dangle + that.__populateFolder(folderModel, folderModel.getWorkspaceId(), folderModel.getFolderId()); + }) + }); } that.fireEvent("openChanged"); return isOpen; @@ -137,37 +141,53 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { }); }, - __addMyWorkspace: function(rootModel) { - const myWorkspaceData = { - label: "My Workspace", - icon: "home", - workspaceId: null, - folderId: null, + __createItemData: function(label, icon, workspaceId, folderId) { + return { + label, + icon, + workspaceId, + folderId, loaded: false, children: [{ label: "Loading...", }], - } + }; + }, + + __addMyWorkspace: function(rootModel) { + const workspaceId = null; + const folderId = null; + const myWorkspaceData = this.__createItemData( + "My Workspace", + "home", + workspaceId, + folderId, + ); const myWorkspaceModel = qx.data.marshal.Json.createModel(myWorkspaceData, true); this.__models.push(myWorkspaceModel); rootModel.getChildren().append(myWorkspaceModel); + + // load next level too + this.__populateFolder(myWorkspaceModel, workspaceId, folderId); }, __addSharedWorkspaces: function(rootModel) { - const sharedWorkspaceData = { - label: "Shared Workspaces", - icon: "shared", - workspaceId: -1, - folderId: null, - loaded: true, - children: [], - } + const workspaceId = -1; + const folderId = null; + const sharedWorkspaceData = this.__createItemData( + "Shared Workspaces", + "shared", + workspaceId, + folderId, + ); const sharedWorkspaceModel = qx.data.marshal.Json.createModel(sharedWorkspaceData, true); this.__models.push(sharedWorkspaceModel); rootModel.getChildren().append(sharedWorkspaceModel); osparc.store.Workspaces.getInstance().fetchWorkspaces() .then(workspaces => { + sharedWorkspaceModel.setLoaded(true); + sharedWorkspaceModel.getChildren().removeAll(); workspaces.forEach(workspace => { this.__addWorkspace(workspace); }); @@ -176,22 +196,23 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { }, __addWorkspace: function(workspace) { - const workspaceData = { - label: "", - icon: "shared", - workspaceId: workspace.getWorkspaceId(), - folderId: null, - loaded: false, - children: [{ - label: "Loading...", - }], - }; + const workspaceId = workspace.getWorkspaceId(); + const folderId = null; + const workspaceData = this.__createItemData( + "", + "shared", + workspaceId, + folderId, + ); const workspaceModel = qx.data.marshal.Json.createModel(workspaceData, true); this.__models.push(workspaceModel); workspace.bind("name", workspaceModel, "label"); const sharedWorkspaceModel = this.__getModel(-1, null); sharedWorkspaceModel.getChildren().append(workspaceModel); + + // load next level too + this.__populateFolder(workspaceModel, workspace.getWorkspaceId(), null); }, __removeWorkspace: function(workspace) { @@ -204,30 +225,32 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { __addFolder: function(folder, parentModel) { const workspaceId = folder.getWorkspaceId(); - const folderData = { - label: "", - icon: workspaceId ? "shared" : "folder", + const folderData = this.__createItemData( + "", + workspaceId ? "shared" : "folder", workspaceId, - folderId: folder.getFolderId(), - loaded: false, - children: [{ - label: "Loading...", - }] - }; + folder.getFolderId(), + ); const folderModel = qx.data.marshal.Json.createModel(folderData, true); this.__models.push(folderModel); folder.bind("name", folderModel, "label"); parentModel.getChildren().push(folderModel); + return folderModel; }, __populateFolder: function(model, workspaceId, folderId) { - osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId) + if (model.getLoaded()) { + return new Promise(resolve => resolve(model.getChildren())); + } + return osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId) .then(folders => { model.setLoaded(true); model.getChildren().removeAll(); + const newFolderModels = []; folders.forEach(folder => { - this.__addFolder(folder, model); + newFolderModels.push(this.__addFolder(folder, model)); }); + return newFolderModels; }); }, 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 46c2626fc1a..42d2013293f 100644 --- a/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js @@ -27,7 +27,7 @@ qx.Class.define("osparc.editor.FolderEditor", { const title = this.getChildControl("title"); title.setRequired(true); manager.add(title); - newFolder ? this.getChildControl("create") : this.getChildControl("save"); + this.__doButton = newFolder ? this.getChildControl("create") : this.getChildControl("save"); this.addListener("appear", this.__onAppear, this); }, @@ -48,6 +48,8 @@ qx.Class.define("osparc.editor.FolderEditor", { }, members: { + __doButton: null, + _createChildControlImpl: function(id) { let control; switch (id) { @@ -69,6 +71,8 @@ qx.Class.define("osparc.editor.FolderEditor", { appearance: "form-button" }); control.addListener("execute", () => { + const title = this.getChildControl("title"); + title.blur(); if (this.__validator.validate()) { control.setFetching(true); this.fireEvent("createFolder"); @@ -83,6 +87,8 @@ qx.Class.define("osparc.editor.FolderEditor", { appearance: "form-button" }); control.addListener("execute", () => { + const title = this.getChildControl("title"); + title.blur(); if (this.__validator.validate()) { control.setFetching(true); this.fireEvent("updateFolder"); @@ -112,6 +118,9 @@ qx.Class.define("osparc.editor.FolderEditor", { const title = this.getChildControl("title"); title.focus(); title.activate(); + + const command = new qx.ui.command.Command("Enter"); + this.__doButton.setCommand(command); } } }); 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 f18610996bd..8beed815e89 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -316,12 +316,7 @@ qx.Class.define("osparc.study.Utils", { return ["UNKNOWN_SERVICES", false].includes(blocked); }, - canMoveToFolder: function(studyData) { - const blocked = this.__getBlockedState(studyData); - return ["UNKNOWN_SERVICES", false].includes(blocked); - }, - - canMoveToWorkspace: function(studyData) { + canMoveTo: function(studyData) { const blocked = this.__getBlockedState(studyData); return ["UNKNOWN_SERVICES", false].includes(blocked); },