From ed110f4cde5090ee83567c726931fb56050d7c0d Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:04:32 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20[Frontend]=20Multiselect=20data?= =?UTF-8?q?=20(#6896)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../class/osparc/dashboard/DataBrowser.js | 2 +- .../source/class/osparc/data/model/Study.js | 11 + .../class/osparc/file/FileLabelWithActions.js | 165 +++++++--- .../source/class/osparc/file/FilePicker.js | 5 +- .../source/class/osparc/file/FileTreeItem.js | 5 + .../source/class/osparc/file/FolderContent.js | 299 ++++++++++++++++++ .../source/class/osparc/file/FolderViewer.js | 283 ++++------------- .../class/osparc/file/TreeFolderView.js | 25 +- .../client/source/class/osparc/store/Data.js | 10 +- .../source/class/osparc/vipMarket/Market.js | 23 +- .../class/osparc/vipMarket/MarketWindow.js | 20 +- .../class/osparc/vipMarket/VipMarket.js | 32 +- .../class/osparc/widget/NodeDataManager.js | 2 +- 13 files changed, 563 insertions(+), 319 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/file/FolderContent.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js index 9d6683a832e..f8ffd3d73d0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js @@ -75,7 +75,7 @@ qx.Class.define("osparc.dashboard.DataBrowser", { const reloadButton = treeFolderView.getChildControl("reload-button"); reloadButton.addListener("execute", () => this.__reloadTree(), this); - const selectedFileLayout = treeFolderView.getChildControl("selected-file-layout"); + const selectedFileLayout = treeFolderView.getChildControl("folder-viewer").getChildControl("selected-file-layout"); selectedFileLayout.addListener("fileDeleted", e => this.__fileDeleted(e.getData()), this); }, 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 e8929c93adf..af4b639cd44 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 @@ -635,6 +635,17 @@ qx.Class.define("osparc.data.model.Study", { return !this.getUi().getSlideshow().isEmpty(); }, + sendMessageToIframe: function(nodeId, msg) { + if (nodeId) { + const node = this.getWorkbench().getNode(nodeId); + if (node && node.getIFrame()) { + node.getIFrame().sendMessageToIframe(msg); + return true; + } + } + return false; + }, + patchStudy: function(studyChanges) { const matches = this.self().OwnPatch.filter(el => Object.keys(studyChanges).indexOf(el) !== -1); if (matches.length) { diff --git a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js index 1343ce73a13..15eec413914 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js +++ b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js @@ -45,18 +45,29 @@ qx.Class.define("osparc.file.FileLabelWithActions", { this.getChildControl("selected-label"); const downloadBtn = this.getChildControl("download-button"); - downloadBtn.addListener("execute", () => this.__retrieveURLAndDownload(), this); + downloadBtn.addListener("execute", () => this.__retrieveURLAndDownloadSelected(), this); const deleteBtn = this.getChildControl("delete-button"); - deleteBtn.addListener("execute", () => this.__deleteFile(), this); + deleteBtn.addListener("execute", () => this.__deleteSelected(), this); }, events: { "fileDeleted": "qx.event.type.Data" }, + properties: { + multiSelect: { + check: "Boolean", + init: false, + nullable: false, + event: "changeMultiSelect", + apply: "__enableMultiSelection", + }, + }, + members: { __selection: null, + __multiSelection: null, _createChildControlImpl: function(id) { let control; @@ -88,26 +99,59 @@ qx.Class.define("osparc.file.FileLabelWithActions", { return control || this.base(arguments, id); }, + __enableMultiSelection: function() { + this.resetItemSelected(); + this.__multiSelection = []; + }, + setItemSelected: function(selectedItem) { - const isFile = osparc.file.FilesTree.isFile(selectedItem); - this.getChildControl("download-button").setEnabled(isFile); - this.getChildControl("delete-button").setEnabled(isFile); - const selectedLabel = this.getChildControl("selected-label"); - if (isFile) { - this.__selection = selectedItem; - selectedLabel.set({ - value: selectedItem.getLabel(), - toolTipText: selectedItem.getFileId() - }); + if (selectedItem) { + const isFile = osparc.file.FilesTree.isFile(selectedItem); + this.getChildControl("download-button").setEnabled(isFile); + this.getChildControl("delete-button").setEnabled(isFile); + const selectedLabel = this.getChildControl("selected-label"); + if (isFile) { + this.__selection = selectedItem; + selectedLabel.set({ + value: selectedItem.getLabel(), + toolTipText: selectedItem.getFileId() + }); + } else { + this.__selection = null; + selectedLabel.set({ + value: "", + toolTipText: "" + }); + } } else { - this.__selection = null; - selectedLabel.set({ - value: "", - toolTipText: "" - }); + this.resetItemSelected(); + } + }, + + setMultiItemSelected: function(multiSelectionData) { + this.__multiSelection = multiSelectionData; + if (multiSelectionData && multiSelectionData.length) { + if (multiSelectionData.length === 1) { + this.setItemSelected(multiSelectionData[0]); + } else { + const selectedLabel = this.getChildControl("selected-label"); + selectedLabel.set({ + value: multiSelectionData.length + " files" + }); + } + } else { + this.resetItemSelected(); } }, + resetItemSelected: function() { + this.__selection = null; + this.__multiSelection = []; + this.getChildControl("download-button").setEnabled(false); + this.getChildControl("delete-button").setEnabled(false); + this.getChildControl("selected-label").resetValue(); + }, + getItemSelected: function() { const selectedItem = this.__selection; if (selectedItem && osparc.file.FilesTree.isFile(selectedItem)) { @@ -116,40 +160,71 @@ qx.Class.define("osparc.file.FileLabelWithActions", { return null; }, - // Request to the server and download - __retrieveURLAndDownload: function() { - let selection = this.getItemSelected(); - if (selection) { - const fileId = selection.getFileId(); - const locationId = selection.getLocation(); - osparc.utils.Utils.retrieveURLAndDownload(locationId, fileId) - .then(data => { - if (data) { - osparc.DownloadLinkTracker.getInstance().downloadLinkUnattended(data.link, data.fileName); + __retrieveURLAndDownloadSelected: function() { + if (this.isMultiSelect()) { + this.__multiSelection.forEach(selection => { + this.__retrieveURLAndDownloadFile(selection); + }); + } else { + const selection = this.getItemSelected(); + if (selection) { + this.__retrieveURLAndDownloadFile(selection); + } + } + }, + + __deleteSelected: function() { + if (this.isMultiSelect()) { + const requests = []; + this.__multiSelection.forEach(selection => { + const request = this.__deleteFile(selection); + if (request) { + requests.push(request); + } + }); + Promise.all(requests) + .then(datas => { + if (datas.length) { + this.fireDataEvent("fileDeleted", datas[0]); + osparc.FlashMessenger.getInstance().logAs(this.tr("Files successfully deleted"), "ERROR"); } }); + requests + } else { + const selection = this.getItemSelected(); + if (selection) { + const request = this.__deleteFile(selection); + if (request) { + request + .then(data => { + this.fireDataEvent("fileDeleted", data); + osparc.FlashMessenger.getInstance().logAs(this.tr("File successfully deleted"), "ERROR"); + }); + } + } } }, - __deleteFile: function() { - let selection = this.getItemSelected(); - if (selection) { - console.log("Delete ", selection); - const fileId = selection.getFileId(); - const locationId = selection.getLocation(); - if (locationId !== 0 && locationId !== "0") { - osparc.FlashMessenger.getInstance().logAs(this.tr("Only files in simcore.s3 can be deleted")); - return false; - } - const dataStore = osparc.store.Data.getInstance(); - dataStore.addListenerOnce("deleteFile", e => { - if (e) { - this.fireDataEvent("fileDeleted", e.getData()); + __retrieveURLAndDownloadFile: function(file) { + const fileId = file.getFileId(); + const locationId = file.getLocation(); + osparc.utils.Utils.retrieveURLAndDownload(locationId, fileId) + .then(data => { + if (data) { + osparc.DownloadLinkTracker.getInstance().downloadLinkUnattended(data.link, data.fileName); } - }, this); - return dataStore.deleteFile(locationId, fileId); + }); + }, + + __deleteFile: function(file) { + const fileId = file.getFileId(); + const locationId = file.getLocation(); + if (locationId !== 0 && locationId !== "0") { + osparc.FlashMessenger.getInstance().logAs(this.tr("Only files in simcore.s3 can be deleted")); + return null; } - return false; - } + const dataStore = osparc.store.Data.getInstance(); + return dataStore.deleteFile(locationId, fileId); + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/file/FilePicker.js b/services/static-webserver/client/source/class/osparc/file/FilePicker.js index 67d67c8455a..fdce6e4aec9 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilePicker.js +++ b/services/static-webserver/client/source/class/osparc/file/FilePicker.js @@ -545,7 +545,8 @@ qx.Class.define("osparc.file.FilePicker", { flex: 1 }); treeFolderLayout.add(treeLayout, 0); - const folderViewer = new osparc.file.FolderViewer(); + const allowMultiselection = false; + const folderViewer = new osparc.file.FolderViewer(allowMultiselection); treeFolderLayout.add(folderViewer, 1); filesTree.addListener("selectionChanged", () => { @@ -562,7 +563,7 @@ qx.Class.define("osparc.file.FilePicker", { const selectionData = e.getData(); this.__selectionChanged(selectionData); }, this); - folderViewer.addListener("itemSelected", e => { + folderViewer.addListener("openItemSelected", e => { const selectionData = e.getData(); this.__selectionChanged(selectionData); if (osparc.file.FilesTree.isFile(selectionData)) { diff --git a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js index 45fe07c10ce..5e7a4a02236 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js @@ -45,6 +45,11 @@ qx.Class.define("osparc.file.FileTreeItem", { construct: function() { this.base(arguments); + this.set({ + indent: 12, // defaults to 19, + decorator: "rounded", + }); + // create a date format like "Oct. 19, 2018 11:31 AM" this._dateFormat = new qx.util.format.DateFormat( qx.locale.Date.getDateFormat("medium") + " " + diff --git a/services/static-webserver/client/source/class/osparc/file/FolderContent.js b/services/static-webserver/client/source/class/osparc/file/FolderContent.js new file mode 100644 index 00000000000..ced22a52c1a --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/file/FolderContent.js @@ -0,0 +1,299 @@ +/* ************************************************************************ + + 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.file.FolderContent", { + extend: qx.ui.container.Stack, + + construct: function() { + this.base(arguments); + + this.getChildControl("icons-layout"); + this.getChildControl("table"); + }, + + properties: { + folder: { + check: "qx.core.Object", + init: null, + nullable: true, + event: "changeFolder", + apply: "__applyFolder" + }, + + mode: { + check: ["list", "icons"], + init: "icons", + nullable: false, + event: "changeMode", + apply: "__reloadFolderContent" + }, + + multiSelect: { + check: "Boolean", + init: false, + nullable: false, + event: "changeMultiSelect", + apply: "__reloadFolderContent" + }, + }, + + events: { + "selectionChanged": "qx.event.type.Data", // tap + "multiSelectionChanged": "qx.event.type.Data", // tap + "openItemSelected": "qx.event.type.Data", // dbltap + "requestDatasetFiles": "qx.event.type.Data", + }, + + statics: { + getItemButton: function() { + const item = new qx.ui.form.ToggleButton().set({ + iconPosition: "top", + width: 100, + height: 80, + padding: 2 + }); + item.getChildControl("label").set({ + font: "text-12", + rich: true, + textAlign: "center", + maxWidth: 100, + maxHeight: 33 // two lines + }); + osparc.utils.Utils.setIdToWidget(item, "FolderViewerItem"); + return item; + }, + + T_POS: { + TYPE: 0, + NAME: 1, + DATE: 2, + SIZE: 3, + ID: 4 + } + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "table": { + const tableModel = new qx.ui.table.model.Simple(); + tableModel.setColumns([ + "", + this.tr("Name"), + this.tr("Date Modified"), + this.tr("Size"), + this.tr("Id") + ]); + control = new osparc.ui.table.Table(tableModel, { + // tableColumnModel: obj => new qx.ui.table.columnmodel.Resize(obj), + initiallyHiddenColumns: [this.self().T_POS.ID] + }); + control.getTableColumnModel().setDataCellRenderer(this.self().T_POS.TYPE, new qx.ui.table.cellrenderer.Image()); + control.setColumnWidth(this.self().T_POS.TYPE, 30); + control.setColumnWidth(this.self().T_POS.NAME, 360); + control.setColumnWidth(this.self().T_POS.DATE, 170); + control.setColumnWidth(this.self().T_POS.SIZE, 70); + this.bind("mode", control, "visibility", { + converter: mode => mode === "list" ? "visible" : "excluded" + }); + const scroll = new qx.ui.container.Scroll(); + scroll.add(control); + this.add(scroll); + break; + } + case "icons-layout": { + control = new qx.ui.container.Composite(new qx.ui.layout.Flow(5, 5)); + osparc.utils.Utils.setIdToWidget(control, "FolderViewerIconsContent"); + this.bind("mode", control, "visibility", { + converter: mode => mode === "icons" ? "visible" : "excluded" + }); + const scroll = new qx.ui.container.Scroll(); + scroll.add(control); + this.add(scroll); + break; + } + } + return control || this.base(arguments, id); + }, + + __convertEntries: function(content) { + const datas = []; + content.forEach(entry => { + const data = { + icon: entry.getIcon ? entry.getIcon() : this.__getIcon(entry), + label: entry.getLabel(), + lastModified: entry.getLastModified ? osparc.utils.Utils.formatDateAndTime(new Date(entry.getLastModified())) : "", + size: entry.getSize ? osparc.utils.Utils.bytesToSize(entry.getSize()) : "", + itemId: entry.getItemId ? entry.getItemId() : null, + entry: entry, + }; + datas.push(data); + }); + const items = []; + if (this.getMode() === "list") { + datas.forEach(data => { + const row = []; + row.push(data["icon"]); + row.push(data["label"]); + row.push(data["lastModified"]); + row.push(data["size"]); + if (data["itemId"]) { + row.push(data["itemId"]); + } + row.entry = data["entry"]; + items.push(row); + }); + } else if (this.getMode() === "icons") { + datas.forEach(data => { + let toolTip = data["label"]; + if (data["size"]) { + toolTip += "
" + data["size"]; + } + if (data["lastModified"]) { + toolTip += "
" + data["lastModified"]; + } + const gridItem = this.self().getItemButton().set({ + label: data["label"], + icon: data["icon"], + toolTipText: toolTip + }); + const icon = gridItem.getChildControl("icon", true); + if (icon.getSource() === "@FontAwesome5Solid/circle-notch/12") { + icon.setPadding(0); + icon.setMarginRight(4); + icon.getContentElement().addClass("rotate"); + } + if (data["itemId"]) { + gridItem.itemId = data["itemId"]; + } + gridItem.entry = data["entry"]; + this.__attachListenersToGridItem(gridItem); + items.push(gridItem); + }); + } + return items; + }, + + __getIcon: function(entry) { + return osparc.file.FilesTree.isDir(entry) ? "@MaterialIcons/folder" : "@MaterialIcons/insert_drive_file"; + }, + + __getEntries: function() { + if (this.getFolder()) { + const children = this.getFolder().getChildren().toArray(); + return this.__convertEntries(children); + } + return []; + }, + + __applyFolder: function(folder) { + if (folder) { + if (folder.getLoaded && !folder.getLoaded()) { + this.fireDataEvent("requestDatasetFiles", { + locationId: folder.getLocation(), + datasetId: folder.getPath() + }); + } + + folder.getChildren().addListener("change", () => { + this.__reloadFolderContent(); + }, this); + } + + this.__reloadFolderContent(); + }, + + __reloadFolderContent: function() { + const entries = this.__getEntries(); + if (this.getMode() === "list") { + const table = this.getChildControl("table"); + table.setData(entries); + this.__attachListenersToTableItem(table); + } else if (this.getMode() === "icons") { + const iconsLayout = this.getChildControl("icons-layout"); + iconsLayout.removeAll(); + const iconsGroup = new qx.ui.form.RadioGroup().set({ + allowEmptySelection: true + }); + entries.forEach(entry => { + if (!this.isMultiSelect()) { + iconsGroup.add(entry); + } + iconsLayout.add(entry); + }); + } + this.setSelection([this.getSelectables()[this.getMode() === "icons" ? 0 : 1]]); + }, + + __itemTapped: function(entry, buttonSelected) { + if (this.isMultiSelect()) { + this.fireDataEvent("multiSelectionChanged", entry); + } else if (buttonSelected === false) { + this.fireDataEvent("selectionChanged", null); + } else { + this.fireDataEvent("selectionChanged", entry); + } + }, + + __itemDblTapped: function(entry) { + this.fireDataEvent("openItemSelected", entry); + }, + + __attachListenersToGridItem: function(gridItem) { + gridItem.addListener("tap", () => { + if (this.isMultiSelect()) { + // pass all buttons that are selected + const selectedFiles = []; + const iconsLayout = this.getChildControl("icons-layout"); + iconsLayout.getChildren().forEach(btn => { + if (osparc.file.FilesTree.isFile(btn.entry) && btn.getValue()) { + selectedFiles.push(btn.entry); + } + }); + this.__itemTapped(selectedFiles, gridItem.getValue()); + } else { + this.__itemTapped(gridItem.entry, gridItem.getValue()); + } + // folders can't be selected + if (osparc.file.FilesTree.isDir(gridItem.entry)) { + gridItem.setValue(false); + } + }, this); + gridItem.addListener("dbltap", () => { + this.__itemDblTapped(gridItem.entry); + }, this); + }, + + __attachListenersToTableItem: function(table) { + table.addListener("cellTap", e => { + const selectedRow = e.getRow(); + const rowData = table.getTableModel().getRowData(selectedRow); + if ("entry" in rowData) { + this.__itemTapped(rowData.entry); + } + }, this); + table.addListener("cellDbltap", e => { + const selectedRow = e.getRow(); + const rowData = table.getTableModel().getRowData(selectedRow); + if ("entry" in rowData) { + this.__itemDblTapped(rowData.entry); + } + }, this); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js index 26fb4433bf3..c44f48cbe27 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js @@ -22,7 +22,7 @@ qx.Class.define("osparc.file.FolderViewer", { extend: qx.ui.core.Widget, - construct: function() { + construct: function(allowMultiselection = true) { this.base(arguments); this._setLayout(new qx.ui.layout.VBox(10)); @@ -32,10 +32,14 @@ qx.Class.define("osparc.file.FolderViewer", { const folderUpBtn = this.getChildControl("folder-up"); folderUpBtn.addListener("execute", () => this.fireDataEvent("folderUp", this.getFolder()), this); this.getChildControl("folder-path"); - this.getChildControl("view-options-icons"); - this.getChildControl("view-options-list"); - this.getChildControl("icons-layout"); - this.getChildControl("table"); + let multiSelectButton = null; + if (allowMultiselection) { + multiSelectButton = this.getChildControl("multi-select-button"); + } + const gridViewButton = this.getChildControl("view-options-icons"); + const listViewButton = this.getChildControl("view-options-list"); + const folderContent = this.getChildControl("folder-content"); + const selectedFileLayout = this.getChildControl("selected-file-layout"); this.bind("folder", this.getChildControl("folder-up"), "enabled", { converter: folder => Boolean(folder && folder.getPathLabel && folder.getPathLabel().length > 1) @@ -44,6 +48,40 @@ qx.Class.define("osparc.file.FolderViewer", { this.bind("folder", this.getChildControl("folder-path"), "value", { converter: folder => folder ? folder.getPathLabel().join(" / ") : this.tr("Select folder") }); + + this.bind("folder", folderContent, "folder"); + + if (allowMultiselection) { + multiSelectButton.addListener("changeValue", e => { + folderContent.setMultiSelect(e.getData()); + selectedFileLayout.setMultiSelect(e.getData()); + }); + } + gridViewButton.addListener("execute", () => { + folderContent.setMode("icons"); + selectedFileLayout.resetItemSelected(); + }); + listViewButton.addListener("execute", () => { + folderContent.setMode("list"); + selectedFileLayout.resetItemSelected(); + }); + + folderContent.addListener("requestDatasetFiles", e => this.fireDataEvent("requestDatasetFiles", e.getData())); + folderContent.addListener("selectionChanged", e => { + const selectionData = e.getData(); + selectedFileLayout.setItemSelected(selectionData); + }, this); + folderContent.addListener("multiSelectionChanged", e => { + const multiSelectionData = e.getData(); + selectedFileLayout.setMultiItemSelected(multiSelectionData); + }, this); + folderContent.addListener("openItemSelected", e => { + const entry = e.getData(); + this.fireDataEvent("openItemSelected", entry); + if (osparc.file.FilesTree.isDir(entry)) { + this.setFolder(entry); + } + }); }, properties: { @@ -52,52 +90,16 @@ qx.Class.define("osparc.file.FolderViewer", { init: null, nullable: true, event: "changeFolder", - apply: "__applyFolder" + apply: "__applyFolder", }, - - mode: { - check: ["list", "icons"], - init: "icons", - nullable: false, - event: "changeMode", - apply: "__reloadFolderContent" - } }, events: { - "selectionChanged": "qx.event.type.Data", // tap - "itemSelected": "qx.event.type.Data", // dbltap + "openItemSelected": "qx.event.type.Data", // dbltap "folderUp": "qx.event.type.Data", "requestDatasetFiles": "qx.event.type.Data" }, - statics: { - getItemButton: function() { - const item = new qx.ui.form.ToggleButton().set({ - iconPosition: "top", - width: 100, - height: 80, - padding: 3 - }); - item.getChildControl("label").set({ - rich: true, - textAlign: "center", - maxWidth: 100, - maxHeight: 31 - }); - osparc.utils.Utils.setIdToWidget(item, "FolderViewerItem"); - return item; - }, - - T_POS: { - TYPE: 0, - NAME: 1, - DATE: 2, - SIZE: 3, - ID: 4 - } - }, - members: { _createChildControlImpl: function(id) { let control; @@ -128,6 +130,15 @@ qx.Class.define("osparc.file.FolderViewer", { }); break; } + case "multi-select-button": { + control = new qx.ui.form.ToggleButton(this.tr("Multiselect")).set({ + value: false, + marginRight: 10, + }); + const header = this.getChildControl("header"); + header.addAt(control, 2); + break; + } case "view-options-rgroup": control = new qx.ui.form.RadioGroup(); break; @@ -135,203 +146,37 @@ qx.Class.define("osparc.file.FolderViewer", { control = new qx.ui.form.ToggleButton(null, "@MaterialIcons/apps/18"); const group = this.getChildControl("view-options-rgroup"); group.add(control); - control.addListener("execute", () => { - this.setMode("icons"); - }); const header = this.getChildControl("header"); - header.addAt(control, 2); + header.addAt(control, 3); break; } case "view-options-list": { control = new qx.ui.form.ToggleButton(null, "@MaterialIcons/reorder/18"); const group = this.getChildControl("view-options-rgroup"); group.add(control); - control.addListener("execute", () => { - this.setMode("list"); - }); const header = this.getChildControl("header"); - header.addAt(control, 3); + header.addAt(control, 4); break; } - case "content-stack": { - control = new qx.ui.container.Stack(); + case "folder-content": { + control = new osparc.file.FolderContent(); this._add(control, { flex: 1 }); break; } - case "table": { - const tableModel = new qx.ui.table.model.Simple(); - tableModel.setColumns([ - "", - this.tr("Name"), - this.tr("Date Modified"), - this.tr("Size"), - this.tr("Id") - ]); - control = new osparc.ui.table.Table(tableModel, { - // tableColumnModel: obj => new qx.ui.table.columnmodel.Resize(obj), - initiallyHiddenColumns: [this.self().T_POS.ID] - }); - control.getTableColumnModel().setDataCellRenderer(this.self().T_POS.TYPE, new qx.ui.table.cellrenderer.Image()); - control.setColumnWidth(this.self().T_POS.TYPE, 30); - control.setColumnWidth(this.self().T_POS.NAME, 360); - control.setColumnWidth(this.self().T_POS.DATE, 170); - control.setColumnWidth(this.self().T_POS.SIZE, 70); - this.bind("mode", control, "visibility", { - converter: mode => mode === "list" ? "visible" : "excluded" - }); - const scroll = new qx.ui.container.Scroll(); - scroll.add(control); - this.getChildControl("content-stack").add(scroll); - break; - } - case "icons-layout": { - control = new qx.ui.container.Composite(new qx.ui.layout.Flow(5, 5)); - osparc.utils.Utils.setIdToWidget(control, "FolderViewerIconsContent"); - this.bind("mode", control, "visibility", { - converter: mode => mode === "icons" ? "visible" : "excluded" + case "selected-file-layout": + control = new osparc.file.FileLabelWithActions().set({ + alignY: "middle" }); - const scroll = new qx.ui.container.Scroll(); - scroll.add(control); - this.getChildControl("content-stack").add(scroll); + this._add(control); break; - } } return control || this.base(arguments, id); }, - __convertEntries: function(content) { - const items = []; - if (this.getMode() === "list") { - content.forEach(entry => { - const row = []; - row.push(entry.getIcon ? entry.getIcon() : this.__getIcon(entry)); - row.push(entry.getLabel()); - row.push(entry.getLastModified ? osparc.utils.Utils.formatDateAndTime(new Date(entry.getLastModified())) : ""); - row.push(entry.getSize ? osparc.utils.Utils.bytesToSize(entry.getSize()) : ""); - if (entry.getItemId) { - row.push(entry.getItemId()); - } - row.entry = entry; - items.push(row); - }); - } else if (this.getMode() === "icons") { - content.forEach(entry => { - let tt = entry.getLabel(); - if (entry.getSize) { - tt += "
" + osparc.utils.Utils.bytesToSize(entry.getSize()); - } - if (entry.getLastModified) { - tt += "
" + osparc.utils.Utils.formatDateAndTime(new Date(entry.getLastModified())); - } - const item = this.self().getItemButton().set({ - label: entry.getLabel(), - icon: entry.getIcon ? entry.getIcon() : this.__getIcon(entry), - toolTipText: tt - }); - const icon = item.getChildControl("icon", true); - if (icon.getSource() === "@FontAwesome5Solid/circle-notch/12") { - icon.setPadding(0); - icon.setMarginRight(4); - icon.getContentElement().addClass("rotate"); - } - - if (entry.getItemId) { - item.itemId = entry.getItemId(); - this.__attachListenersToItems(item, entry); - } - items.push(item); - }); - } - return items; - }, - - __getIcon: function(entry) { - return osparc.file.FilesTree.isDir(entry) ? "@MaterialIcons/folder" : "@MaterialIcons/insert_drive_file"; - }, - - __getEntries: function() { - if (this.getFolder()) { - const children = this.getFolder().getChildren().toArray(); - return this.__convertEntries(children); - } - return []; - }, - - __applyFolder: function(folder) { - if (folder) { - if (folder.getLoaded && !folder.getLoaded()) { - this.fireDataEvent("requestDatasetFiles", { - locationId: folder.getLocation(), - datasetId: folder.getPath() - }); - } - - folder.getChildren().addListener("change", () => { - this.__reloadFolderContent(); - }, this); - } - - this.__reloadFolderContent(); - }, - - __reloadFolderContent: function() { - const entries = this.__getEntries(); - if (this.getMode() === "list") { - const table = this.getChildControl("table"); - table.setData(entries); - this.__attachListenersTotable(table); - } else if (this.getMode() === "icons") { - const iconsLayout = this.getChildControl("icons-layout"); - iconsLayout.removeAll(); - const iconsGroup = new qx.ui.form.RadioGroup().set({ - allowEmptySelection: true - }); - entries.forEach(entry => { - iconsGroup.add(entry); - iconsLayout.add(entry); - }); - } - const stack = this.getChildControl("content-stack"); - stack.setSelection([stack.getSelectables()[this.getMode() === "icons" ? 0 : 1]]); - }, - - __itemTapped: function(item) { - this.fireDataEvent("selectionChanged", item); - }, - - __itemDblTapped: function(item) { - this.fireDataEvent("itemSelected", item); - if (osparc.file.FilesTree.isDir(item)) { - this.setFolder(item); - } - }, - - __attachListenersToItems: function(btn, entry) { - btn.addListener("tap", () => { - this.__itemTapped(entry); - }, this); - btn.addListener("dbltap", () => { - this.__itemDblTapped(entry); - }, this); - }, - - __attachListenersTotable: function(table) { - table.addListener("cellTap", e => { - const selectedRow = e.getRow(); - const rowData = table.getTableModel().getRowData(selectedRow); - if ("entry" in rowData) { - this.__itemTapped(rowData.entry); - } - }, this); - table.addListener("cellDbltap", e => { - const selectedRow = e.getRow(); - const rowData = table.getTableModel().getRowData(selectedRow); - if ("entry" in rowData) { - this.__itemDblTapped(rowData.entry); - } - }, this); + __applyFolder: function() { + this.getChildControl("selected-file-layout").resetItemSelected(); } } }); diff --git a/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js b/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js index 9e88b2d688e..d9349713218 100644 --- a/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js +++ b/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js @@ -75,12 +75,6 @@ qx.Class.define("osparc.file.TreeFolderView", { treeFolderLayout.add(control, 1); break; } - case "selected-file-layout": - control = new osparc.file.FileLabelWithActions().set({ - alignY: "middle" - }); - this._add(control); - break; } return control || this.base(arguments, id); }, @@ -89,27 +83,16 @@ qx.Class.define("osparc.file.TreeFolderView", { this.getChildControl("reload-button"); const folderTree = this.getChildControl("folder-tree"); const folderViewer = this.getChildControl("folder-viewer"); - const selectedFileLayout = this.getChildControl("selected-file-layout"); // Connect elements folderTree.addListener("selectionChanged", () => { - const selectionData = folderTree.getSelectedItem(); - if (selectionData) { - selectedFileLayout.setItemSelected(selectionData); - if (osparc.file.FilesTree.isDir(selectionData) || (selectionData.getChildren && selectionData.getChildren().length)) { - folderViewer.setFolder(selectionData); - } - } - }, this); - - folderViewer.addListener("selectionChanged", e => { - const selectionData = e.getData(); - if (selectionData) { - selectedFileLayout.setItemSelected(selectionData); + const selectedFolder = folderTree.getSelectedItem(); + if (selectedFolder && (osparc.file.FilesTree.isDir(selectedFolder) || (selectedFolder.getChildren && selectedFolder.getChildren().length))) { + folderViewer.setFolder(selectedFolder); } }, this); - folderViewer.addListener("itemSelected", e => { + folderViewer.addListener("openItemSelected", e => { const data = e.getData(); folderTree.openNodeAndParents(data); folderTree.setSelection(new qx.data.Array([data])); diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index b514b7b3144..322dc1313bd 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -33,7 +33,6 @@ qx.Class.define("osparc.store.Data", { events: { "fileCopied": "qx.event.type.Data", - "deleteFile": "qx.event.type.Data" }, members: { @@ -286,7 +285,7 @@ qx.Class.define("osparc.store.Data", { deleteFile: function(locationId, fileUuid) { if (!osparc.data.Permissions.getInstance().canDo("study.node.data.delete", true)) { - return false; + return null; } // Deletes File @@ -296,22 +295,19 @@ qx.Class.define("osparc.store.Data", { fileUuid: encodeURIComponent(fileUuid) } }; - osparc.data.Resources.fetch("storageFiles", "delete", params) + return osparc.data.Resources.fetch("storageFiles", "delete", params) .then(files => { const data = { data: files, locationId: locationId, fileUuid: fileUuid }; - this.fireDataEvent("deleteFile", data); + return data; }) .catch(err => { console.error(err); osparc.FlashMessenger.getInstance().logAs(this.tr("Failed deleting file"), "ERROR"); - this.fireDataEvent("deleteFile", null); }); - - return true; } } }); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js index 8bd65242eb2..dbffeefed8e 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js @@ -18,7 +18,7 @@ qx.Class.define("osparc.vipMarket.Market", { extend: osparc.ui.window.TabbedView, - construct: function() { + construct: function(category) { this.base(arguments); const miniWallet = osparc.desktop.credits.BillingCenter.createMiniWalletView().set({ @@ -51,9 +51,17 @@ qx.Class.define("osparc.vipMarket.Market", { }].forEach(marketInfo => { this.__buildViPMarketPage(marketInfo); }); + + if (category) { + this.openCategory(category); + } }); }, + events: { + "importMessageSent": "qx.event.type.Data", + }, + properties: { openBy: { check: "String", @@ -70,6 +78,7 @@ qx.Class.define("osparc.vipMarket.Market", { metadataUrl: marketInfo["url"], }); this.bind("openBy", vipMarketView, "openBy"); + vipMarketView.addListener("importMessageSent", () => this.fireEvent("importMessageSent")); const page = this.addTab(marketInfo["label"], marketInfo["icon"], vipMarketView); page.category = marketInfo["category"]; return page; @@ -83,5 +92,17 @@ qx.Class.define("osparc.vipMarket.Market", { } return false; }, + + sendCloseMessage: function() { + const store = osparc.store.Store.getInstance(); + const currentStudy = store.getCurrentStudy(); + const nodeId = this.getOpenBy(); + if (currentStudy && nodeId) { + const msg = { + "type": "closeMarket", + }; + currentStudy.sendMessageToIframe(nodeId, msg); + } + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js b/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js index c610abf36b3..c238f5618b8 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js @@ -30,20 +30,22 @@ qx.Class.define("osparc.vipMarket.MarketWindow", { height }); - const vipMarket = this.__vipMarket = new osparc.vipMarket.Market().set({ + const vipMarket = this.__vipMarket = new osparc.vipMarket.Market(category).set({ openBy: nodeId ? nodeId : null, }); this._setTabbedView(vipMarket); - - if (category) { - vipMarket.openCategory(category); - } }, statics: { openWindow: function(nodeId, category) { if (osparc.product.Utils.showS4LStore()) { const storeWindow = new osparc.vipMarket.MarketWindow(nodeId, category); + storeWindow.getVipMarket().addListener("importMessageSent", () => storeWindow.close()); + storeWindow.addListenerOnce("close", () => { + if (storeWindow.getVipMarket()) { + storeWindow.getVipMarket().sendCloseMessage(); + } + }); storeWindow.center(); storeWindow.open(); return storeWindow; @@ -51,4 +53,12 @@ qx.Class.define("osparc.vipMarket.MarketWindow", { return null; } }, + + members: { + __vipMarket: null, + + getVipMarket: function() { + return this.__vipMarket; + }, + }, }); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js index 1385264933e..f7a0125f3c0 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js @@ -26,6 +26,10 @@ qx.Class.define("osparc.vipMarket.VipMarket", { this.__buildLayout(); }, + events: { + "importMessageSent": "qx.event.type.Data" + }, + properties: { openBy: { check: "String", @@ -71,7 +75,6 @@ qx.Class.define("osparc.vipMarket.VipMarket", { members: { __anatomicalModels: null, - __purchasesItems: null, __anatomicalModelsModel: null, _createChildControlImpl: function(id) { @@ -214,7 +217,6 @@ qx.Class.define("osparc.vipMarket.VipMarket", { .then(values => { const licensedItems = values[0]; const purchasesItems = values[1]; - this.__purchasesItems = purchasesItems; this.__anatomicalModels = []; allAnatomicalModels.forEach(model => { @@ -359,22 +361,18 @@ qx.Class.define("osparc.vipMarket.VipMarket", { }, __sendImportModelMessage: function(modelId) { + const store = osparc.store.Store.getInstance(); + const currentStudy = store.getCurrentStudy(); const nodeId = this.getOpenBy(); - if (nodeId) { - const store = osparc.store.Store.getInstance(); - const currentStudy = store.getCurrentStudy(); - if (!currentStudy) { - return; - } - const node = currentStudy.getWorkbench().getNode(nodeId); - if (node && node.getIFrame()) { - const msg = { - "type": "importModel", - "message": { - "modelId": modelId, - }, - }; - node.getIFrame().sendMessageToIframe(msg); + if (currentStudy && nodeId) { + const msg = { + "type": "importModel", + "message": { + "modelId": modelId, + }, + }; + if (currentStudy.sendMessageToIframe(nodeId, msg)) { + this.fireEvent("importMessageSent"); } } }, diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js b/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js index 641e98c58a0..2785f658c6c 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js +++ b/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js @@ -94,7 +94,7 @@ qx.Class.define("osparc.widget.NodeDataManager", { const reloadButton = treeFolderView.getChildControl("reload-button"); reloadButton.addListener("execute", () => this.__reloadTree(), this); - const selectedFileLayout = treeFolderView.getChildControl("selected-file-layout"); + const selectedFileLayout = treeFolderView.getChildControl("folder-viewer").getChildControl("selected-file-layout"); selectedFileLayout.addListener("fileDeleted", e => this.__fileDeleted(e.getData()), this); },