diff --git a/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js index 69293492585..d374a28f99b 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js +++ b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js @@ -72,7 +72,6 @@ qx.Class.define("osparc.jobs.ActivityCenterWindow", { }); this.addListener("close", () => { - runsBrowser.stopInterval(); subRunsBrowser.stopInterval(); }); }, diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js b/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js index c5ab4355df9..2636961004d 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js @@ -24,6 +24,8 @@ qx.Class.define("osparc.jobs.RunsBrowser", { this._setLayout(new qx.ui.layout.VBox(10)); + const reloadButton = this.getChildControl("reload-button"); + reloadButton.addListener("execute", () => this.reloadRuns()); this.getChildControl("intro-label"); const jobsFilter = this.getChildControl("jobs-filter"); const runningCB = this.getChildControl("running-only-cb"); @@ -35,8 +37,6 @@ qx.Class.define("osparc.jobs.RunsBrowser", { }); runningCB.bind("value", runsTable, "runningOnly"); - - this.__reloadInterval = setInterval(() => this.reloadRuns(), 10*1000); }, events: { @@ -49,13 +49,19 @@ qx.Class.define("osparc.jobs.RunsBrowser", { _createChildControlImpl: function(id) { let control; switch (id) { - case "header-filter": + case "header-toolbar": control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)); this._add(control); break; + case "reload-button": + control = new qx.ui.form.Button(this.tr("Reload"), "@FontAwesome5Solid/sync-alt/14"); + this.getChildControl("header-toolbar").add(control); + break; case "intro-label": - control = new qx.ui.basic.Label(this.tr("Select a Run to check the details")); - this.getChildControl("header-filter").add(control); + control = new qx.ui.basic.Label(this.tr("Select a Run to check the details")).set({ + alignY: "middle", + }); + this.getChildControl("header-toolbar").add(control); break; case "jobs-filter": control = new osparc.filter.TextFilter("text", "jobsList").set({ @@ -66,7 +72,7 @@ qx.Class.define("osparc.jobs.RunsBrowser", { placeholder: qx.locale.Manager.tr("Filter by name or ID"), }); control.hide(); // @matusdrobuliak66: remove this when the backend is ready - this.getChildControl("header-filter").add(control, { + this.getChildControl("header-toolbar").add(control, { flex: 1 }); break; @@ -75,7 +81,7 @@ qx.Class.define("osparc.jobs.RunsBrowser", { value: true, label: qx.locale.Manager.tr("Active only"), }); - this.getChildControl("header-filter").add(control); + this.getChildControl("header-toolbar").add(control); break; case "runs-table": { const projectUuid = null; @@ -95,11 +101,5 @@ qx.Class.define("osparc.jobs.RunsBrowser", { const runsTable = this.getChildControl("runs-table"); runsTable.reloadRuns(); }, - - stopInterval: function() { - if (this.__reloadInterval) { - clearInterval(this.__reloadInterval); - } - }, } }) diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js b/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js index f11bb85e402..1e620708b1b 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js @@ -79,12 +79,8 @@ qx.Class.define("osparc.jobs.RunsTableModel", { }, }, - statics: { - SERVER_MAX_LIMIT: 49, - }, - members: { - __includeChildren: false, + __includeChildren: null, // overridden sortByColumn(columnIndex, ascending) { @@ -126,7 +122,7 @@ qx.Class.define("osparc.jobs.RunsTableModel", { const lastRow = Math.min(qxLastRow, this._rowCount - 1); // Returns a request promise with given offset and limit const getFetchPromise = (offset, limit) => { - const orderBy = this.getOrderBy(); + const orderBy = this.getOrderBy(); let promise; if (this.getProjectUuid()) { promise = osparc.store.Jobs.getInstance().fetchJobsHistory(this.getProjectUuid(), this.__includeChildren, offset, limit, orderBy); @@ -153,15 +149,13 @@ qx.Class.define("osparc.jobs.RunsTableModel", { }; // Divides the model row request into several server requests to comply with the number of rows server limit + const serverMaxLimit = osparc.store.Jobs.SERVER_MAX_LIMIT; const reqLimit = lastRow - firstRow + 1; // Number of requested rows - let nRequests = Math.ceil(reqLimit / this.self().SERVER_MAX_LIMIT); + let nRequests = Math.ceil(reqLimit / serverMaxLimit); if (nRequests > 1) { const requests = []; - for (let i=firstRow; i <= lastRow; i += this.self().SERVER_MAX_LIMIT) { - // fetch the first page only - if (i < 1) { - requests.push(getFetchPromise(i, i > lastRow - this.self().SERVER_MAX_LIMIT + 1 ? reqLimit % this.self().SERVER_MAX_LIMIT : this.self().SERVER_MAX_LIMIT)) - } + for (let i=firstRow; i <= lastRow; i += serverMaxLimit) { + requests.push(getFetchPromise(i, i > lastRow - serverMaxLimit + 1 ? reqLimit % serverMaxLimit : serverMaxLimit)); } Promise.all(requests) .then(responses => this._onRowDataLoaded(responses.flat())) diff --git a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js index 2c43fa59ed9..58ad6040cfb 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js @@ -57,10 +57,6 @@ qx.Class.define("osparc.jobs.SubRunsTableModel", { }, }, - statics: { - SERVER_MAX_LIMIT: 49, - }, - members: { // overridden sortByColumn(columnIndex, ascending) { @@ -132,12 +128,13 @@ qx.Class.define("osparc.jobs.SubRunsTableModel", { }; // Divides the model row request into several server requests to comply with the number of rows server limit + const serverMaxLimit = osparc.store.Jobs.SERVER_MAX_LIMIT; const reqLimit = lastRow - firstRow + 1; // Number of requested rows - const nRequests = Math.ceil(reqLimit / this.self().SERVER_MAX_LIMIT); + const nRequests = Math.ceil(reqLimit / serverMaxLimit); if (nRequests > 1) { const requests = []; - for (let i=firstRow; i <= lastRow; i += this.self().SERVER_MAX_LIMIT) { - requests.push(getFetchPromise(i, i > lastRow - this.self().SERVER_MAX_LIMIT + 1 ? reqLimit % this.self().SERVER_MAX_LIMIT : this.self().SERVER_MAX_LIMIT)) + for (let i=firstRow; i <= lastRow; i += serverMaxLimit) { + requests.push(getFetchPromise(i, i > lastRow - serverMaxLimit + 1 ? reqLimit % serverMaxLimit : serverMaxLimit)) } Promise.all(requests) .then(responses => this._onRowDataLoaded(responses.flat())) diff --git a/services/static-webserver/client/source/class/osparc/theme/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/Appearance.js index c0a1a3da81c..2dcb2db7d46 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -1192,8 +1192,8 @@ qx.Theme.define("osparc.theme.Appearance", { padding: [5, 10], // showTimeout is themeable so it can be tuned // it was defaulted to 700 which was too short - showTimeout: 2000, - hideTimeout: 6000, + showTimeout: 1400, + hideTimeout: 5000, }) }, diff --git a/services/static-webserver/client/source/class/osparc/ui/table/cellrenderer/ImageButtonRenderer.js b/services/static-webserver/client/source/class/osparc/ui/table/cellrenderer/ImageButtonRenderer.js index 8b9fd7896bd..43cfd6b8b05 100644 --- a/services/static-webserver/client/source/class/osparc/ui/table/cellrenderer/ImageButtonRenderer.js +++ b/services/static-webserver/client/source/class/osparc/ui/table/cellrenderer/ImageButtonRenderer.js @@ -21,6 +21,8 @@ qx.Class.define("osparc.ui.table.cellrenderer.ImageButtonRenderer", { construct: function(clickAction, iconPath) { this.base(arguments, clickAction); + this.__imageCache = {}; + this.setIconPath(iconPath); }, @@ -34,11 +36,43 @@ qx.Class.define("osparc.ui.table.cellrenderer.ImageButtonRenderer", { }, members: { + __imageCache: null, + __applyIconPath: function(iconPath) { const resMgr = qx.util.ResourceManager.getInstance(); - const iconUrl = resMgr.toUri(iconPath); // Resolves to the correct URL of the asset + const iconUrl = resMgr.toUri(iconPath); + + // Create a data URI or use a more cache-friendly approach + // Use base64 encoding for small icons (best for caching) + this.__loadImageAsDataUri(iconUrl, iconPath); + }, + + __loadImageAsDataUri: function(iconUrl, iconPath) { + if (this.__imageCache[iconPath]) { + this.setButtonContent(this.__imageCache[iconPath]); + return; + } + + // Fetch and convert to data URI for permanent caching + fetch(iconUrl) + .then(response => response.blob()) + .then(blob => { + const reader = new FileReader(); + reader.onload = () => { + const dataUri = reader.result; + const content = `icon`; - this.setButtonContent(`icon`); + // Cache the data URI + this.__imageCache[iconPath] = content; + this.setButtonContent(content); + }; + reader.readAsDataURL(blob); + }) + .catch(err => { + console.warn("Failed to cache icon as data URI:", iconPath, err); + // Fallback to original method + this.setButtonContent(`icon`); + }); }, } });