From bc4d0dff355789d46654c59e08db05c44f324fe6 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 13:37:46 +0200 Subject: [PATCH 1/8] UserThumbnail and UserDetails --- .../class/osparc/ui/basic/UserThumbnail.js | 48 +++++++ .../source/class/osparc/user/UserDetails.js | 132 ++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js create mode 100644 services/static-webserver/client/source/class/osparc/user/UserDetails.js diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js new file mode 100644 index 00000000000..b9add207d4e --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js @@ -0,0 +1,48 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.ui.basic.UserThumbnail", { + extend: qx.ui.basic.Image, + + construct: function(size) { + this.base(arguments); + + this.set(osparc.utils.Utils.getThumbnailProps(size)); + + if (osparc.data.Permissions.getInstance().isProductOwner()) { + this.setCursor("pointer"); + this.addListener("tap", this.__openUserDetails, this); + } + }, + + properties: { + user: { + check: "osparc.data.model.User", + init: true, + nullable: true, + apply: "__applyUser", + } + }, + + members: { + __openUserDetails: function() { + if (this.getUser()) { + osparc.user.UserDetails.popUpInWindow(this.getUser()); + } + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/user/UserDetails.js b/services/static-webserver/client/source/class/osparc/user/UserDetails.js new file mode 100644 index 00000000000..0e0fbd99dea --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/user/UserDetails.js @@ -0,0 +1,132 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2022 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.user.UserDetails", { + extend: osparc.ui.window.Window, + + construct: function(user) { + this.base(arguments); + + this.setLayout(new qx.ui.layout.VBox(10)); + + this.setUser(user); + }, + + statics: { + WIDTH: 300, + HEIGHT: 200, + + popUpInWindow: function(userModel) { + const userDetails = new osparc.user.UserDetails(userModel); + const title = userModel.getUsername(); + osparc.ui.window.Window.popUpInWindow(userDetails, title, this.WIDTH, this.HEIGHT).set({ + // layout: new qx.ui.layout.Grow(), + // ...osparc.ui.window.TabbedWindow.DEFAULT_PROPS, + }); + }, + + GRID_POS: { + USERNAME: 0, + FULLNAME: 1, + EMAIL: 2, + } + }, + + properties: { + user: { + check: "osparc.data.model.User", + init: true, + nullable: false, + event: "changeUser", + apply: "__applyUser", + } + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "top-layout": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10)); + this._add(control); + break; + case "thumbnail": + control = new osparc.ui.basic.Thumbnail(null, 100, 100); + this.getChildControl("top-layout").add(control); + break; + case "main-info": { + const grid = new qx.ui.layout.Grid(5, 10); + grid.setColumnFlex(1, 1); + control = new qx.ui.container.Composite(grid); + this.getChildControl("top-layout").add(control, { + flex: 1 + }); + break; + } + case "username": { + const title = new qx.ui.basic.Label("Username"); + this.getChildControl("main-info").add(title, { + row: this.self().GRID_POS.USERNAME, + column: 0 + }); + control = new qx.ui.basic.Label(); + this.getChildControl("main-info").add(control, { + row: this.self().GRID_POS.USERNAME, + column: 1 + }); + break; + } + case "fullname": { + const title = new qx.ui.basic.Label("Full Name"); + this.getChildControl("main-info").add(title, { + row: this.self().GRID_POS.FULLNAME, + column: 0 + }); + control = new qx.ui.basic.Label(); + this.getChildControl("main-info").add(control, { + row: this.self().GRID_POS.FULLNAME, + column: 1 + }); + break; + } + case "email": { + const title = new qx.ui.basic.Label("Email"); + this.getChildControl("main-info").add(title, { + row: this.self().GRID_POS.EMAIL, + column: 0 + }); + control = new qx.ui.basic.Label(); + this.getChildControl("main-info").add(control, { + row: this.self().GRID_POS.EMAIL, + column: 1 + }); + break; + } + } + return control || this.base(arguments, id); + }, + + __applyUser: function(user) { + console.log("user", user); + + this.getChildControl("thumbnail").setSource(user.getThumbnail()); + this.getChildControl("username").setValue(user.getUsername()); + this.getChildControl("fullname").setValue(user.getFirstName() + " " + this.getLastName()); + this.getChildControl("email").setValue(user.getEmail()); + }, + } +}); From 1c1621790415aaa47d7b71379709a48bf83661e3 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 13:37:53 +0200 Subject: [PATCH 2/8] minor --- .../client/source/class/osparc/store/Support.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Support.js b/services/static-webserver/client/source/class/osparc/store/Support.js index a7dad622d0c..58e71f81301 100644 --- a/services/static-webserver/client/source/class/osparc/store/Support.js +++ b/services/static-webserver/client/source/class/osparc/store/Support.js @@ -163,7 +163,7 @@ qx.Class.define("osparc.store.Support", { addReleaseNotesToMenu: function(menu) { const releaseTag = osparc.utils.Utils.getReleaseTag(); const releaseLink = osparc.utils.Utils.getReleaseLink(); - const releaseBtn = new qx.ui.menu.Button(qx.locale.Manager.tr("Release Notes") + " " + releaseTag, "@FontAwesome5Solid/book/14"); + const releaseBtn = new qx.ui.menu.Button(qx.locale.Manager.tr("What's new in") + " " + releaseTag, "@FontAwesome5Solid/bullhorn/14"); releaseBtn.addListener("execute", () => window.open(releaseLink), this); menu.add(releaseBtn); }, From 7630abc749fe5d15748fb818cb58f9e3b016d47d Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 13:41:22 +0200 Subject: [PATCH 3/8] minor --- .../client/source/class/osparc/conversation/MessageUI.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js b/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js index 27414094300..8aaab2b901b 100644 --- a/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js +++ b/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js @@ -64,7 +64,7 @@ qx.Class.define("osparc.conversation.MessageUI", { let control; switch (id) { case "thumbnail": - control = osparc.utils.Utils.createThumbnail(32).set({ + control = new osparc.ui.basic.UserThumbnail(32).set({ marginTop: 4, }); this._add(control, { @@ -167,6 +167,7 @@ qx.Class.define("osparc.conversation.MessageUI", { .then(user => { if (user) { thumbnail.setSource(user.getThumbnail()); + thumbnail.setUser(user); userName.setValue(user.getLabel()); } else { thumbnail.setSource(osparc.utils.Avatar.emailToThumbnail()); From 25544ad4afbba536b1c6855f25c148f2f1391b0a Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 15:05:45 +0200 Subject: [PATCH 4/8] UserDetails working --- .../class/osparc/conversation/MessageUI.js | 10 ++----- .../source/class/osparc/data/model/User.js | 14 +++++++-- .../class/osparc/ui/basic/UserThumbnail.js | 12 +++++++- .../source/class/osparc/user/UserDetails.js | 30 ++++++++++--------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js b/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js index 8aaab2b901b..1264f9e7a8c 100644 --- a/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js +++ b/services/static-webserver/client/source/class/osparc/conversation/MessageUI.js @@ -165,14 +165,8 @@ qx.Class.define("osparc.conversation.MessageUI", { osparc.store.Users.getInstance().getUser(message["userGroupId"]) .then(user => { - if (user) { - thumbnail.setSource(user.getThumbnail()); - thumbnail.setUser(user); - userName.setValue(user.getLabel()); - } else { - thumbnail.setSource(osparc.utils.Avatar.emailToThumbnail()); - userName.setValue("Unknown user"); - } + thumbnail.setUser(user); + userName.setValue(user ? user.getLabel() : "Unknown user"); }) .catch(() => { thumbnail.setSource(osparc.utils.Avatar.emailToThumbnail()); diff --git a/services/static-webserver/client/source/class/osparc/data/model/User.js b/services/static-webserver/client/source/class/osparc/data/model/User.js index 14ecd3a05d8..6d571b98aba 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/User.js +++ b/services/static-webserver/client/source/class/osparc/data/model/User.js @@ -51,7 +51,7 @@ qx.Class.define("osparc.data.model.User", { } description += email; } - const thumbnail = osparc.utils.Avatar.emailToThumbnail(email, username); + this.set({ userId, groupId, @@ -60,10 +60,14 @@ qx.Class.define("osparc.data.model.User", { lastName, email, phoneNumber: userData["phone"] || null, - thumbnail, label: userData["userName"] || description, description, }); + + // create the thumbnail after setting email and username + this.set({ + thumbnail: this.createThumbnail(), + }); }, properties: { @@ -137,4 +141,10 @@ qx.Class.define("osparc.data.model.User", { event: "changeThumbnail", }, }, + + members: { + createThumbnail: function(size) { + return osparc.utils.Avatar.emailToThumbnail(this.getEmail(), this.getUsername(), size); + }, + }, }); diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js index b9add207d4e..56a858cb5a9 100644 --- a/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js +++ b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js @@ -39,9 +39,19 @@ qx.Class.define("osparc.ui.basic.UserThumbnail", { }, members: { + __applyUser: function(user) { + if (user) { + this.setSource(user.getThumbnail()); + } else { + this.setSource(osparc.utils.Avatar.emailToThumbnail()); + } + }, + __openUserDetails: function() { if (this.getUser()) { - osparc.user.UserDetails.popUpInWindow(this.getUser()); + const userDetails = new osparc.user.UserDetails(this.getUser()); + userDetails.center(); + userDetails.open(); } }, } diff --git a/services/static-webserver/client/source/class/osparc/user/UserDetails.js b/services/static-webserver/client/source/class/osparc/user/UserDetails.js index 0e0fbd99dea..06b3e20187c 100644 --- a/services/static-webserver/client/source/class/osparc/user/UserDetails.js +++ b/services/static-webserver/client/source/class/osparc/user/UserDetails.js @@ -21,7 +21,13 @@ qx.Class.define("osparc.user.UserDetails", { construct: function(user) { this.base(arguments); - this.setLayout(new qx.ui.layout.VBox(10)); + this.set({ + layout: new qx.ui.layout.VBox(10), + autoDestroy: true, + showMaximize: false, + showMinimize: false, + clickAwayClose: true, + }); this.setUser(user); }, @@ -30,15 +36,6 @@ qx.Class.define("osparc.user.UserDetails", { WIDTH: 300, HEIGHT: 200, - popUpInWindow: function(userModel) { - const userDetails = new osparc.user.UserDetails(userModel); - const title = userModel.getUsername(); - osparc.ui.window.Window.popUpInWindow(userDetails, title, this.WIDTH, this.HEIGHT).set({ - // layout: new qx.ui.layout.Grow(), - // ...osparc.ui.window.TabbedWindow.DEFAULT_PROPS, - }); - }, - GRID_POS: { USERNAME: 0, FULLNAME: 1, @@ -62,15 +59,20 @@ qx.Class.define("osparc.user.UserDetails", { switch (id) { case "top-layout": control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10)); - this._add(control); + this.add(control); break; case "thumbnail": control = new osparc.ui.basic.Thumbnail(null, 100, 100); + control.getChildControl("image").set({ + anonymous: true, + decorator: "rounded", + }); this.getChildControl("top-layout").add(control); break; case "main-info": { const grid = new qx.ui.layout.Grid(5, 10); grid.setColumnFlex(1, 1); + grid.setColumnAlign(0, "right", "middle"); control = new qx.ui.container.Composite(grid); this.getChildControl("top-layout").add(control, { flex: 1 @@ -121,11 +123,11 @@ qx.Class.define("osparc.user.UserDetails", { }, __applyUser: function(user) { - console.log("user", user); + this.setCaption(user.getUsername()); - this.getChildControl("thumbnail").setSource(user.getThumbnail()); + this.getChildControl("thumbnail").setSource(user.createThumbnail(96)); this.getChildControl("username").setValue(user.getUsername()); - this.getChildControl("fullname").setValue(user.getFirstName() + " " + this.getLastName()); + this.getChildControl("fullname").setValue(user.getFirstName() + " " + user.getLastName()); this.getChildControl("email").setValue(user.getEmail()); }, } From 7c79c57e70a41ceccf58b73f0d2d354a602edb84 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 15:11:18 +0200 Subject: [PATCH 5/8] last changes --- .../source/class/osparc/user/UserDetails.js | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/user/UserDetails.js b/services/static-webserver/client/source/class/osparc/user/UserDetails.js index 06b3e20187c..aa28f1ee54b 100644 --- a/services/static-webserver/client/source/class/osparc/user/UserDetails.js +++ b/services/static-webserver/client/source/class/osparc/user/UserDetails.js @@ -40,6 +40,8 @@ qx.Class.define("osparc.user.UserDetails", { USERNAME: 0, FULLNAME: 1, EMAIL: 2, + USER_ID: 3, + GROUP_ID: 4, } }, @@ -58,7 +60,7 @@ qx.Class.define("osparc.user.UserDetails", { let control; switch (id) { case "top-layout": - control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10)); + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(20)); this.add(control); break; case "thumbnail": @@ -70,7 +72,7 @@ qx.Class.define("osparc.user.UserDetails", { this.getChildControl("top-layout").add(control); break; case "main-info": { - const grid = new qx.ui.layout.Grid(5, 10); + const grid = new qx.ui.layout.Grid(10, 6); grid.setColumnFlex(1, 1); grid.setColumnAlign(0, "right", "middle"); control = new qx.ui.container.Composite(grid); @@ -118,6 +120,32 @@ qx.Class.define("osparc.user.UserDetails", { }); break; } + case "user-id": { + const title = new qx.ui.basic.Label("User ID"); + this.getChildControl("main-info").add(title, { + row: this.self().GRID_POS.USER_ID, + column: 0 + }); + control = new qx.ui.basic.Label(); + this.getChildControl("main-info").add(control, { + row: this.self().GRID_POS.USER_ID, + column: 1 + }); + break; + } + case "group-id": { + const title = new qx.ui.basic.Label("Group ID"); + this.getChildControl("main-info").add(title, { + row: this.self().GRID_POS.GROUP_ID, + column: 0 + }); + control = new qx.ui.basic.Label(); + this.getChildControl("main-info").add(control, { + row: this.self().GRID_POS.GROUP_ID, + column: 1 + }); + break; + } } return control || this.base(arguments, id); }, @@ -129,6 +157,8 @@ qx.Class.define("osparc.user.UserDetails", { this.getChildControl("username").setValue(user.getUsername()); this.getChildControl("fullname").setValue(user.getFirstName() + " " + user.getLastName()); this.getChildControl("email").setValue(user.getEmail()); + this.getChildControl("user-id").setValue(String(user.getUserId())); + this.getChildControl("group-id").setValue(String(user.getGroupId())); }, } }); From 9b02b5ab29840af8ef6bf0e67b31169e4edd81e9 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Mon, 1 Sep 2025 15:19:04 +0200 Subject: [PATCH 6/8] Update services/static-webserver/client/source/class/osparc/user/UserDetails.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../client/source/class/osparc/user/UserDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/user/UserDetails.js b/services/static-webserver/client/source/class/osparc/user/UserDetails.js index aa28f1ee54b..7c4ea92a8d1 100644 --- a/services/static-webserver/client/source/class/osparc/user/UserDetails.js +++ b/services/static-webserver/client/source/class/osparc/user/UserDetails.js @@ -155,7 +155,7 @@ qx.Class.define("osparc.user.UserDetails", { this.getChildControl("thumbnail").setSource(user.createThumbnail(96)); this.getChildControl("username").setValue(user.getUsername()); - this.getChildControl("fullname").setValue(user.getFirstName() + " " + user.getLastName()); + this.getChildControl("fullname").setValue([user.getFirstName(), user.getLastName()].filter(Boolean).join(" ")); this.getChildControl("email").setValue(user.getEmail()); this.getChildControl("user-id").setValue(String(user.getUserId())); this.getChildControl("group-id").setValue(String(user.getGroupId())); From dfbb68e7c6ec181b1be899183cc3b53119b20077 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 15:20:08 +0200 Subject: [PATCH 7/8] init values --- .../client/source/class/osparc/ui/basic/UserThumbnail.js | 2 +- .../client/source/class/osparc/user/UserDetails.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js index 56a858cb5a9..6b4de75cfac 100644 --- a/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js +++ b/services/static-webserver/client/source/class/osparc/ui/basic/UserThumbnail.js @@ -32,7 +32,7 @@ qx.Class.define("osparc.ui.basic.UserThumbnail", { properties: { user: { check: "osparc.data.model.User", - init: true, + init: null, nullable: true, apply: "__applyUser", } diff --git a/services/static-webserver/client/source/class/osparc/user/UserDetails.js b/services/static-webserver/client/source/class/osparc/user/UserDetails.js index aa28f1ee54b..a9ee5570d31 100644 --- a/services/static-webserver/client/source/class/osparc/user/UserDetails.js +++ b/services/static-webserver/client/source/class/osparc/user/UserDetails.js @@ -5,7 +5,7 @@ https://osparc.io Copyright: - 2022 IT'IS Foundation, https://itis.swiss + 2025 IT'IS Foundation, https://itis.swiss License: MIT: https://opensource.org/licenses/MIT @@ -48,7 +48,7 @@ qx.Class.define("osparc.user.UserDetails", { properties: { user: { check: "osparc.data.model.User", - init: true, + init: null, nullable: false, event: "changeUser", apply: "__applyUser", From 804c18a9ee59b2d2a5a3a8188740e4bab0ba02c4 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 1 Sep 2025 15:30:18 +0200 Subject: [PATCH 8/8] fix --- .../client/source/class/osparc/dashboard/ResourceBrowserBase.js | 1 + 1 file changed, 1 insertion(+) 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 e553e65cd02..bd718b59ddf 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -737,6 +737,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { win.close(); } win.addListener("cancel", () => cancelStudyOptions()); + win.getChildControl("close-button").addListener("tap", () => cancelStudyOptions()); studyOptions.addListener("cancel", () => cancelStudyOptions()); studyOptions.addListener("startStudy", () => { const newName = studyOptions.getChildControl("title-field").getValue();