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);
},