From fee208dde4d2aed552ee4431cd22493cc95312a6 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:03:44 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20[Frontend]=20Search=20users=20f?= =?UTF-8?q?or=20sharing=20(#6974)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/class/osparc/dashboard/CardBase.js | 98 ++++++------- .../source/class/osparc/data/Resources.js | 18 ++- .../client/source/class/osparc/data/Roles.js | 25 +++- .../source/class/osparc/data/model/Study.js | 6 +- .../source/class/osparc/data/model/User.js | 38 ++--- .../desktop/organizations/MembersList.js | 89 ++++++++---- .../organizations/OrganizationsList.js | 8 +- .../osparc/desktop/wallets/MembersList.js | 3 - .../source/class/osparc/filter/TextFilter.js | 35 +++-- .../source/class/osparc/po/PreRegistration.js | 2 +- .../client/source/class/osparc/po/Users.js | 2 +- .../class/osparc/share/AddCollaborators.js | 6 - .../osparc/share/NewCollaboratorsManager.js | 131 ++++++++++-------- .../source/class/osparc/store/Groups.js | 67 +++++---- .../client/source/class/osparc/store/Users.js | 77 ++++++++++ 15 files changed, 387 insertions(+), 218 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/store/Users.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index a6d8742f2c3..2be758253a0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -148,54 +148,59 @@ qx.Class.define("osparc.dashboard.CardBase", { return false; }, - // groups -> [orgMembs, orgs, [productEveryone], [everyone]]; - setIconAndTooltip: function(shareIcon, accessRights, groups) { - shareIcon.setSource(osparc.dashboard.CardBase.SHARE_ICON); - if (osparc.data.model.Study.canIWrite(accessRights)) { - shareIcon.set({ - toolTipText: qx.locale.Manager.tr("Share") - }); + populateShareIcon: async function(shareIcon, accessRights) { + const gids = Object.keys(accessRights).map(key => parseInt(key)); + + const groupsStore = osparc.store.Groups.getInstance(); + + // Icon + const groupEveryone = groupsStore.getEveryoneGroup(); + const groupProductEveryone = groupsStore.getEveryoneProductGroup(); + const organizations = groupsStore.getOrganizations(); + const organizationIds = Object.keys(organizations).map(key => parseInt(key)); + if (gids.includes(groupEveryone.getGroupId()) || gids.includes(groupProductEveryone.getGroupId())) { + shareIcon.setSource(osparc.dashboard.CardBase.SHARED_ALL); + } else if (organizationIds.filter(value => gids.includes(value)).length) { // find intersection + shareIcon.setSource(osparc.dashboard.CardBase.SHARED_ORGS); + } else if (gids.length === 1) { + shareIcon.setSource(osparc.dashboard.CardBase.SHARE_ICON); + } else { + shareIcon.setSource(osparc.dashboard.CardBase.SHARED_USER); } - let sharedGrps = []; - const myGroupId = osparc.auth.Data.getInstance().getGroupId(); - for (let i=0; i { + const idx = gids.indexOf(group.getGroupId()); + if (idx > -1) { + sharedGrps.push(group); + gids.splice(idx, 1); } - const sharedGrp = []; - const gids = Object.keys(accessRights); - for (let j=0; j group.getGroupId() === gid); - if (grp) { - sharedGrp.push(grp); + }); + // once the groups were removed, the remaining group ids are users' primary groups ids + const usersStore = osparc.store.Users.getInstance(); + const myGroupId = groupsStore.getMyGroupId(); + for (let i=0; i hint.show(), this); shareIcon.addListener("mouseout", () => hint.exclude(), this); }, - - // groups -> [orgMembs, orgs, [productEveryone], [everyone]]; - populateShareIcon: function(shareIcon, accessRights) { - const groupsStore = osparc.store.Groups.getInstance(); - const orgMembs = Object.values(groupsStore.getReachableUsers()); - const orgs = Object.values(groupsStore.getOrganizations()); - const productEveryone = [groupsStore.getEveryoneProductGroup()]; - const everyone = [groupsStore.getEveryoneGroup()]; - const groups = [orgMembs, orgs, productEveryone, everyone]; - osparc.dashboard.CardBase.setIconAndTooltip(shareIcon, accessRights, groups); - }, }, properties: { diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 8d1ab230ee4..bc9068bca8c 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -878,6 +878,22 @@ qx.Class.define("osparc.data.Resources", { } } }, + /* + * USERS + */ + "users": { + useCache: false, // osparc.store.Groups handles the cache + endpoints: { + get: { + method: "GET", + url: statics.API + "/groups/{gid}/users" + }, + search: { + method: "POST", + url: statics.API + "/users:search" + } + } + }, /* * WALLETS */ @@ -958,7 +974,7 @@ qx.Class.define("osparc.data.Resources", { } } }, - "users": { + "poUsers": { endpoints: { search: { method: "GET", diff --git a/services/static-webserver/client/source/class/osparc/data/Roles.js b/services/static-webserver/client/source/class/osparc/data/Roles.js index b3a87e6c1c4..6b172bf80f1 100644 --- a/services/static-webserver/client/source/class/osparc/data/Roles.js +++ b/services/static-webserver/client/source/class/osparc/data/Roles.js @@ -165,7 +165,7 @@ qx.Class.define("osparc.data.Roles", { } }, - __createIntoFromRoles: function(roles, showWording = true) { + __createRolesLayout: function(roles, showWording = true) { const rolesLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)).set({ alignY: "middle", paddingRight: 10 @@ -202,23 +202,34 @@ qx.Class.define("osparc.data.Roles", { }, createRolesOrgInfo: function() { - return this.__createIntoFromRoles(osparc.data.Roles.ORG); + return this.__createRolesLayout(osparc.data.Roles.ORG); }, createRolesWalletInfo: function() { - return this.__createIntoFromRoles(osparc.data.Roles.WALLET); + return this.__createRolesLayout(osparc.data.Roles.WALLET); }, createRolesStudyInfo: function() { - return this.__createIntoFromRoles(osparc.data.Roles.STUDY); + return this.__createRolesLayout(osparc.data.Roles.STUDY); }, createRolesServicesInfo: function() { - return this.__createIntoFromRoles(osparc.data.Roles.SERVICES); + return this.__createRolesLayout(osparc.data.Roles.SERVICES); }, createRolesWorkspaceInfo: function(showWording = true) { - return this.__createIntoFromRoles(osparc.data.Roles.WORKSPACE, showWording); - } + return this.__createRolesLayout(osparc.data.Roles.WORKSPACE, showWording); + }, + + replaceSpacerWithWidget: function(rolesLayout, widget) { + if (rolesLayout && rolesLayout.getChildren()) { + // remove spacer + rolesLayout.remove(rolesLayout.getChildren()[0]); + // add widget + rolesLayout.addAt(widget, 0, { + flex: 1 + }); + } + }, } }); 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 af4b639cd44..f03a01ff741 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 @@ -274,10 +274,9 @@ qx.Class.define("osparc.data.model.Study", { }, canIWrite: function(studyAccessRights) { - const myGroupId = osparc.auth.Data.getInstance().getGroupId(); const groupsStore = osparc.store.Groups.getInstance(); const orgIDs = groupsStore.getOrganizationIds(); - orgIDs.push(myGroupId); + orgIDs.push(groupsStore.getMyGroupId()); if (orgIDs.length) { return osparc.share.CollaboratorsStudy.canGroupsWrite(studyAccessRights, (orgIDs)); } @@ -285,10 +284,9 @@ qx.Class.define("osparc.data.model.Study", { }, canIDelete: function(studyAccessRights) { - const myGroupId = osparc.auth.Data.getInstance().getGroupId(); const groupsStore = osparc.store.Groups.getInstance(); const orgIDs = groupsStore.getOrganizationIds(); - orgIDs.push(myGroupId); + orgIDs.push(groupsStore.getMyGroupId()); if (orgIDs.length) { return osparc.share.CollaboratorsStudy.canGroupsDelete(studyAccessRights, (orgIDs)); } 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 fbdc80c6adf..7294987345c 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 @@ -28,28 +28,30 @@ qx.Class.define("osparc.data.model.User", { construct: function(userData) { this.base(arguments); - let description = ""; - if (userData["first_name"]) { - description = userData["first_name"]; - if (userData["last_name"]) { - description += " " + userData["last_name"]; + const userId = ("id" in userData) ? parseInt(userData["id"]) : parseInt(userData["userId"]); + const groupId = ("gid" in userData) ? parseInt(userData["gid"]) : parseInt(userData["groupId"]); + const username = userData["userName"]; + const email = ("login" in userData) ? userData["login"] : userData["email"]; + const firstName = ("first_name" in userData) ? userData["first_name"] : userData["firstName"]; + const lastName = ("last_name" in userData) ? userData["last_name"] : userData["lastName"]; + let description = [firstName, lastName].join(" ").trim(); // the null values will be replaced by empty strings + if (email) { + if (description) { + description += " - " } - description += " - "; + description += email; } - if (userData["login"]) { - description += userData["login"]; - } - const thumbnail = osparc.utils.Avatar.emailToThumbnail(userData["login"], userData["userName"]); + const thumbnail = osparc.utils.Avatar.emailToThumbnail(email, username); this.set({ - userId: parseInt(userData["id"]), - groupId: parseInt(userData["gid"]), - username: userData["userName"], - firstName: userData["first_name"], - lastName: userData["last_name"], - email: userData["login"], - label: userData["userName"], - description, + userId, + groupId, + username, + firstName, + lastName, + email, thumbnail, + label: username, + description, }); }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js index acd68c25680..91ef4845139 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js @@ -23,13 +23,8 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { this._setLayout(new qx.ui.layout.VBox(10)); - this._add(this.__createIntroText()); - this._add(this.__getMemberInvitation()); - this._add(this.__getRolesToolbar()); - this._add(this.__getMembersFilter()); - this._add(this.__getMembersList(), { - flex: 1 - }); + this.__createNewMemberLayout(); + this.__createMembersList(); }, statics: { @@ -80,7 +75,7 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { members: { __currentOrg: null, __introLabel: null, - __memberInvitation: null, + __addMembersButton: null, __membersModel: null, setCurrentOrg: function(orgModel) { @@ -91,7 +86,29 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { this.__reloadOrgMembers(); }, - __createIntroText: function() { + __createNewMemberLayout: function() { + const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + vBox.add(this.__createAddMembersText()); + vBox.add(this.__getMemberInvitation()); + this._add(vBox); + }, + + __createMembersList: function() { + const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + const rolesLayout = this.__getRolesToolbar(); + const membersFilter = this.__getMembersFilter(); + membersFilter.setPaddingRight(10); + osparc.data.Roles.replaceSpacerWithWidget(rolesLayout, membersFilter); + vBox.add(rolesLayout); + vBox.add(this.__getMembersList(), { + flex: 1 + }); + this._add(vBox, { + flex: 1 + }); + }, + + __createAddMembersText: function() { const intro = this.__introLabel = new qx.ui.basic.Label().set({ alignX: "left", rich: true, @@ -101,25 +118,39 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { }, __getMemberInvitation: function() { - const hBox = this.__memberInvitation = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ - alignY: "middle" - })); - - const newMemberUserName = new qx.ui.form.TextField().set({ - required: true, - placeholder: this.tr(" New Member's username") - }); - hBox.add(newMemberUserName, { - flex: 1 + const addBtn = this.__addMembersButton = new qx.ui.form.Button().set({ + appearance: "strong-button", + label: this.tr("Add Members..."), + allowGrowX: false, }); - - const addBtn = new qx.ui.form.Button(this.tr("Add")); addBtn.addListener("execute", function() { - this.__addMember(newMemberUserName.getValue()); + const serializedData = this.__currentOrg.serialize(); + serializedData["resourceType"] = "organization"; + const showOrganizations = false; + const collaboratorsManager = new osparc.share.NewCollaboratorsManager(serializedData, showOrganizations); + collaboratorsManager.setCaption("Add Members"); + collaboratorsManager.getActionButton().setLabel(this.tr("Add")); + collaboratorsManager.addListener("addCollaborators", e => { + const selectedMembers = e.getData(); + if (selectedMembers.length) { + const promises = []; + const usersStore = osparc.store.Users.getInstance(); + selectedMembers.forEach(selectedMemberGId => promises.push(usersStore.getUser(selectedMemberGId))); + Promise.all(promises) + .then(users => { + users.forEach(user => this.__addMember(user.getUsername())); + }) + .catch(err => { + console.error(err); + }) + .finally(collaboratorsManager.close()); + } else { + collaboratorsManager.close(); + } + }, this); }, this); - hBox.add(addBtn); - return hBox; + return addBtn; }, __getRolesToolbar: function() { @@ -127,10 +158,8 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { }, __getMembersFilter: function() { - const filter = new osparc.filter.TextFilter("text", "organizationMembersList").set({ - allowStretchX: true, - margin: [0, 10, 5, 10] - }); + const filter = new osparc.filter.TextFilter("text", "organizationMembersList"); + filter.setCompact(true); return filter; }, @@ -212,11 +241,11 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { const canIDelete = organization.getAccessRights()["delete"]; const introText = canIWrite ? - this.tr("You can add new members and promote or demote existing ones.
In order to add new members, type their username or email if this is public.") : + this.tr("You can add new members and change their roles.") : this.tr("You can't add new members to this Organization. Please contact an Administrator or Manager."); this.__introLabel.setValue(introText); - this.__memberInvitation.set({ + this.__addMembersButton.set({ enabled: canIWrite }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js index ff773341ff0..c86917fed36 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js @@ -37,8 +37,12 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { }); this._add(intro); - this._add(this.__getOrganizationsFilter()); - this._add(osparc.data.Roles.createRolesOrgInfo()); + const rolesLayout = osparc.data.Roles.createRolesOrgInfo(); + const orgsFilter = this.__getOrganizationsFilter(); + orgsFilter.setPaddingRight(10); + osparc.data.Roles.replaceSpacerWithWidget(rolesLayout, orgsFilter); + this._add(rolesLayout); + this._add(this.__getOrganizationsList(), { flex: 1 }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js b/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js index dc27a0cfee3..a55b47e9ec4 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js @@ -120,9 +120,6 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { const vBox = this.__memberInvitation = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); vBox.exclude(); - const label = new qx.ui.basic.Label(this.tr("Select from the list below and click Share")); - vBox.add(label); - const addMemberBtn = new qx.ui.form.Button(this.tr("Add Members...")).set({ appearance: "strong-button", allowGrowX: false diff --git a/services/static-webserver/client/source/class/osparc/filter/TextFilter.js b/services/static-webserver/client/source/class/osparc/filter/TextFilter.js index 4566bb86f5d..7e31f4740af 100644 --- a/services/static-webserver/client/source/class/osparc/filter/TextFilter.js +++ b/services/static-webserver/client/source/class/osparc/filter/TextFilter.js @@ -35,8 +35,7 @@ qx.Class.define("osparc.filter.TextFilter", { allowStretchY: false }); - this.__textField = this.getChildControl("textfield"); - + this.getChildControl("textfield"); this.getChildControl("clearbutton"); this.__attachEventHandlers(); @@ -46,18 +45,23 @@ qx.Class.define("osparc.filter.TextFilter", { appearance: { refine: true, init: "textfilter" - } + }, + + compact: { + check: "Boolean", + init: false, + apply: "__applyCompact", + }, }, members: { - __textField: null, - /** * Function that resets the field and dispatches the update. */ reset: function() { - this.__textField.resetValue(); - this.__textField.fireDataEvent("input", ""); + const textField = this.getChildControl("textfield"); + textField.resetValue(); + textField.fireDataEvent("input", ""); }, _createChildControlImpl: function(id) { @@ -78,7 +82,7 @@ qx.Class.define("osparc.filter.TextFilter", { case "clearbutton": control = new osparc.ui.basic.IconButton("@MaterialIcons/close/12", () => { this.reset(); - this.__textField.focus(); + this.getChildControl("textfield").focus(); }); this._add(control, { right: 0, @@ -89,8 +93,21 @@ qx.Class.define("osparc.filter.TextFilter", { return control || this.base(arguments, id); }, + __applyCompact: function(compact) { + this.set({ + allowStretchX: compact, + allowGrowX: compact, + maxHeight: compact ? 30 : null, + margin: compact ? 0 : null, + }); + + this.getChildControl("textfield").set({ + margin: compact ? 0 : null, + }); + }, + __attachEventHandlers: function() { - this.__textField.addListener("input", evt => { + this.getChildControl("textfield").addListener("input", evt => { this._filterChange(evt.getData().trim().toLowerCase()); }); } diff --git a/services/static-webserver/client/source/class/osparc/po/PreRegistration.js b/services/static-webserver/client/source/class/osparc/po/PreRegistration.js index f2fe853b1df..8a1f0e767df 100644 --- a/services/static-webserver/client/source/class/osparc/po/PreRegistration.js +++ b/services/static-webserver/client/source/class/osparc/po/PreRegistration.js @@ -99,7 +99,7 @@ qx.Class.define("osparc.po.PreRegistration", { return } - osparc.data.Resources.fetch("users", "preRegister", params) + osparc.data.Resources.fetch("poUsers", "preRegister", params) .then(data => { if (data.length) { findingStatus.setValue(this.tr("Pre-Registered as:")); diff --git a/services/static-webserver/client/source/class/osparc/po/Users.js b/services/static-webserver/client/source/class/osparc/po/Users.js index feef74218f1..eb011712b42 100644 --- a/services/static-webserver/client/source/class/osparc/po/Users.js +++ b/services/static-webserver/client/source/class/osparc/po/Users.js @@ -83,7 +83,7 @@ qx.Class.define("osparc.po.Users", { email: userEmail.getValue() } }; - osparc.data.Resources.fetch("users", "search", params) + osparc.data.Resources.fetch("poUsers", "search", params) .then(data => { findingStatus.setValue(data.length + this.tr(" user(s) found")); this.__populateFoundUsersLayout(data); diff --git a/services/static-webserver/client/source/class/osparc/share/AddCollaborators.js b/services/static-webserver/client/source/class/osparc/share/AddCollaborators.js index da0394cd010..788650a1af3 100644 --- a/services/static-webserver/client/source/class/osparc/share/AddCollaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/AddCollaborators.js @@ -51,10 +51,6 @@ qx.Class.define("osparc.share.AddCollaborators", { _createChildControlImpl: function(id) { let control; switch (id) { - case "intro-text": - control = new qx.ui.basic.Label(this.tr("Select from the list below and click Share")); - this._add(control); - break; case "buttons-layout": control = new qx.ui.container.Composite(new qx.ui.layout.HBox()); this._add(control); @@ -89,8 +85,6 @@ qx.Class.define("osparc.share.AddCollaborators", { }, __buildLayout: function() { - this.getChildControl("intro-text"); - const addCollaboratorBtn = this.getChildControl("share-with"); addCollaboratorBtn.addListener("execute", () => { const collaboratorsManager = new osparc.share.NewCollaboratorsManager(this.__serializedDataCopy); diff --git a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js index fcbe5befff5..ee149c77422 100644 --- a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js +++ b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js @@ -11,7 +11,7 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { construct: function(resourceData, showOrganizations = true) { this.base(arguments, "collaboratorsManager", this.tr("Share with")); this.set({ - layout: new qx.ui.layout.VBox(), + layout: new qx.ui.layout.VBox(5), allowMinimize: false, allowMaximize: false, showMinimize: false, @@ -29,8 +29,8 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { this.__renderLayout(); this.__selectedCollaborators = []; - this.__visibleCollaborators = {}; - this.__reloadCollaborators(); + this.__potentialCollaborators = {}; + this.__reloadPotentialCollaborators(); this.center(); this.open(); @@ -43,34 +43,43 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { members: { __resourceData: null, __showOrganizations: null, - __introLabel: null, __textFilter: null, + __searchButton: null, __collabButtonsContainer: null, - __orgsButton: null, __shareButton: null, __selectedCollaborators: null, - __visibleCollaborators: null, + __potentialCollaborators: null, getActionButton: function() { return this.__shareButton; }, __renderLayout: function() { - const introText = this.tr("In order to start Sharing with other members, you first need to belong to an Organization."); - const introLabel = this.__introLabel = new qx.ui.basic.Label(introText).set({ + const introLabel = new qx.ui.basic.Label().set({ + value: this.tr("Select users or organizations from the list bellow. Search them if they aren't listed."), rich: true, wrap: true, - visibility: "excluded", - padding: 8 + paddingBottom: 5 }); this.add(introLabel); - const filter = this.__textFilter = new osparc.filter.TextFilter("name", "collaboratorsManager").set({ - allowStretchX: true, - margin: [0, 10, 5, 10] - }); + const toolbar = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + alignY: "middle", + })); + const filter = this.__textFilter = new osparc.filter.TextFilter("name", "collaboratorsManager"); + filter.setCompact(true); this.addListener("appear", () => filter.getChildControl("textfield").focus()); - this.add(filter); + toolbar.add(filter, { + flex: 1 + }); + const searchButton = this.__searchButton = new osparc.ui.form.FetchButton(this.tr("Search"), "@FontAwesome5Solid/search/12").set({ + maxHeight: 30, + }); + const command = new qx.ui.command.Command("Enter"); + searchButton.setCommand(command); + searchButton.addListener("execute", () => this.__searchUsers(), this); + toolbar.add(searchButton); + this.add(toolbar); const collabButtonsContainer = this.__collabButtonsContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox()); const scrollContainer = new qx.ui.container.Scroll(); @@ -82,13 +91,6 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { const buttons = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({ alignX: "right" })); - // Quick access for users that still don't belong to any organization - const orgsButton = this.__orgsButton = new qx.ui.form.Button(this.tr("My Organizations...")).set({ - appearance: "form-button", - visibility: "excluded", - }); - orgsButton.addListener("execute", () => osparc.desktop.organizations.OrganizationsWindow.openWindow(), this); - buttons.add(orgsButton); const shareButton = this.__shareButton = new osparc.ui.form.FetchButton(this.tr("Share")).set({ appearance: "form-button", enabled: false, @@ -98,42 +100,57 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { this.add(buttons); }, - __reloadCollaborators: function() { - let includeProductEveryone = false; + __searchUsers: function() { + const text = this.__textFilter.getChildControl("textfield").getValue(); + this.__searchButton.setFetching(true); + osparc.store.Users.getInstance().searchUsers(text) + .then(users => { + users.forEach(user => user["collabType"] = 2); + this.__addPotentialCollaborators(users); + }) + .catch(err => { + console.error(err); + osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); + }) + .finally(() => this.__searchButton.setFetching(false)); + }, + + __showProductEveryone: function() { + let showProductEveryone = false; if (this.__showOrganizations === false) { - includeProductEveryone = false; + showProductEveryone = false; } else if (this.__resourceData && this.__resourceData["resourceType"] === "study") { // studies can't be shared with ProductEveryone - includeProductEveryone = false; + showProductEveryone = false; } else if (this.__resourceData && this.__resourceData["resourceType"] === "template") { // only users with permissions can share templates with ProductEveryone - includeProductEveryone = osparc.data.Permissions.getInstance().canDo("study.everyone.share"); + showProductEveryone = osparc.data.Permissions.getInstance().canDo("study.everyone.share"); } else if (this.__resourceData && this.__resourceData["resourceType"] === "service") { // all users can share services with ProductEveryone - includeProductEveryone = true; + showProductEveryone = true; } - const potentialCollaborators = osparc.store.Groups.getInstance().getPotentialCollaborators(false, includeProductEveryone) - this.__visibleCollaborators = potentialCollaborators; - const anyCollaborator = Object.keys(potentialCollaborators).length; - // tell the user that belonging to an organization is required to start sharing - this.__introLabel.setVisibility(anyCollaborator ? "excluded" : "visible"); - this.__orgsButton.setVisibility(anyCollaborator ? "excluded" : "visible"); - - // or start sharing - this.__textFilter.setVisibility(anyCollaborator ? "visible" : "excluded"); - this.__collabButtonsContainer.setVisibility(anyCollaborator ? "visible" : "excluded"); - this.__shareButton.setVisibility(anyCollaborator ? "visible" : "excluded"); - this.__addEditors(); + return showProductEveryone; + }, + + __reloadPotentialCollaborators: function() { + const includeProductEveryone = this.__showProductEveryone(); + this.__potentialCollaborators = osparc.store.Groups.getInstance().getPotentialCollaborators(false, includeProductEveryone); + const potentialCollaborators = Object.values(this.__potentialCollaborators); + this.__addPotentialCollaborators(potentialCollaborators); }, __collaboratorButton: function(collaborator) { const collaboratorButton = new osparc.filter.CollaboratorToggleButton(collaborator); + collaboratorButton.groupId = collaborator.getGroupId(); collaboratorButton.addListener("changeValue", e => { const selected = e.getData(); if (selected) { this.__selectedCollaborators.push(collaborator.getGroupId()); } else { - this.__selectedCollaborators.remove(collaborator.getGroupId()); + const idx = this.__selectedCollaborators.indexOf(collaborator.getGroupId()); + if (idx > -1) { + this.__selectedCollaborators.splice(idx, 1); + } } this.__shareButton.setEnabled(Boolean(this.__selectedCollaborators.length)); }, this); @@ -141,11 +158,9 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { return collaboratorButton; }, - __addEditors: function() { - const visibleCollaborators = Object.values(this.__visibleCollaborators); - + __addPotentialCollaborators: function(potentialCollaborators) { // sort them first - visibleCollaborators.sort((a, b) => { + potentialCollaborators.sort((a, b) => { if (a["collabType"] > b["collabType"]) { return 1; } @@ -159,27 +174,35 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { }); let existingCollabs = []; - if (this.__resourceData && this.__resourceData["accessRights"]) { - // study/template/service/wallet - if (this.__resourceData["resourceType"] === "wallet") { + if (this.__resourceData) { + if (this.__resourceData["groupMembers"] && this.__resourceData["resourceType"] === "organization") { + // organization + existingCollabs = Object.keys(this.__resourceData["groupMembers"]); + } else if (this.__resourceData["accessRights"] && this.__resourceData["resourceType"] === "wallet") { + // wallet // array of objects existingCollabs = this.__resourceData["accessRights"].map(collab => collab["gid"]); - } else { + } else if (this.__resourceData["accessRights"]) { + // study/template/service/ // object existingCollabs = Object.keys(this.__resourceData["accessRights"]); } } const existingCollaborators = existingCollabs.map(c => parseInt(c)); - visibleCollaborators.forEach(visibleCollaborator => { - // do not list the visibleCollaborators that are already collaborators - if (existingCollaborators.includes(visibleCollaborator.getGroupId())) { + potentialCollaborators.forEach(potentialCollaborator => { + // do not list the potentialCollaborators that are already collaborators + if (existingCollaborators.includes(potentialCollaborator.getGroupId())) { + return; + } + // do not list those that were already listed + if (this.__collabButtonsContainer.getChildren().find(c => "groupId" in c && c["groupId"] === potentialCollaborator.getGroupId())) { return; } - if (this.__showOrganizations === false && visibleCollaborator["collabType"] !== 2) { + if (this.__showOrganizations === false && potentialCollaborator["collabType"] !== 2) { return; } - this.__collabButtonsContainer.add(this.__collaboratorButton(visibleCollaborator)); + this.__collabButtonsContainer.add(this.__collaboratorButton(potentialCollaborator)); }); }, diff --git a/services/static-webserver/client/source/class/osparc/store/Groups.js b/services/static-webserver/client/source/class/osparc/store/Groups.js index e954de7aba6..e897d6b4285 100644 --- a/services/static-webserver/client/source/class/osparc/store/Groups.js +++ b/services/static-webserver/client/source/class/osparc/store/Groups.js @@ -45,11 +45,6 @@ qx.Class.define("osparc.store.Groups", { check: "osparc.data.model.Group", init: {} }, - - reachableUsers: { - check: "Object", - init: {} - }, }, events: { @@ -115,8 +110,7 @@ qx.Class.define("osparc.store.Groups", { // reset group's group members group.setGroupMembers({}); orgMembers.forEach(orgMember => { - const user = new osparc.data.model.UserMember(orgMember); - this.__addToUsersCache(user, groupId); + this.__addMemberToCache(orgMember, groupId); }); } }); @@ -126,8 +120,9 @@ qx.Class.define("osparc.store.Groups", { return new Promise(resolve => { this.__fetchGroups() .then(orgs => { - // reset Reachable Users - this.resetReachableUsers(); + // reset Users + const usersStore = osparc.store.Users.getInstance(); + usersStore.resetUsers(); const promises = Object.keys(orgs).map(orgId => this.__fetchGroupMembers(orgId)); Promise.all(promises) .then(() => resolve()) @@ -152,8 +147,9 @@ qx.Class.define("osparc.store.Groups", { allGroupsAndUsers[organization.getGroupId()] = organization; }); - Object.values(this.getReachableUsers()).forEach(reachableUser => { - allGroupsAndUsers[reachableUser.getGroupId()] = reachableUser; + const users = osparc.store.Users.getInstance().getUsers(); + users.forEach(user => { + allGroupsAndUsers[user.getGroupId()] = user; }); return allGroupsAndUsers; @@ -174,9 +170,11 @@ qx.Class.define("osparc.store.Groups", { groupMe["collabType"] = 2; groups.push(groupMe); - Object.values(this.getReachableUsers()).forEach(member => { - member["collabType"] = 2; - groups.push(member); + const usersStore = osparc.store.Users.getInstance(); + const users = usersStore.getUsers(); + users.forEach(user => { + user["collabType"] = 2; + groups.push(user); }); Object.values(this.getOrganizations()).forEach(org => { @@ -202,6 +200,12 @@ qx.Class.define("osparc.store.Groups", { const potentialCollaborators = {}; const orgs = this.getOrganizations(); const productEveryone = this.getEveryoneProductGroup(); + + if (includeProductEveryone && productEveryone) { + productEveryone["collabType"] = 0; + potentialCollaborators[productEveryone.getGroupId()] = productEveryone; + } + Object.values(orgs).forEach(org => { if (org.getAccessRights()["read"]) { // maybe because of migration script, some users have access to the product everyone group @@ -213,20 +217,20 @@ qx.Class.define("osparc.store.Groups", { potentialCollaborators[org.getGroupId()] = org; } }); - const members = this.getReachableUsers(); - for (const gid of Object.keys(members)) { - members[gid]["collabType"] = 2; - potentialCollaborators[gid] = members[gid]; - } + if (includeMe) { const myGroup = this.getGroupMe(); myGroup["collabType"] = 2; potentialCollaborators[myGroup.getGroupId()] = myGroup; } - if (includeProductEveryone && productEveryone) { - productEveryone["collabType"] = 0; - potentialCollaborators[productEveryone.getGroupId()] = productEveryone; - } + + const usersStore = osparc.store.Users.getInstance(); + const users = usersStore.getUsers(); + users.forEach(user => { + user["collabType"] = 2; + potentialCollaborators[user.getGroupId()] = user; + }); + return potentialCollaborators; }, @@ -240,16 +244,18 @@ qx.Class.define("osparc.store.Groups", { getUserByUserId: function(userId) { if (userId) { - const visibleMembers = this.getReachableUsers(); - return Object.values(visibleMembers).find(member => member.getUserId() === userId); + const usersStore = osparc.store.Users.getInstance(); + const users = usersStore.getUsers(); + return users.find(user => user.getUserId() === userId); } return null; }, getUserByGroupId: function(groupId) { if (groupId) { - const visibleMembers = this.getReachableUsers(); - return Object.values(visibleMembers).find(member => member.getGroupId() === groupId); + const usersStore = osparc.store.Users.getInstance(); + const users = usersStore.getUsers(); + return users.find(user => user.getGroupId() === groupId); } return null; }, @@ -419,14 +425,15 @@ qx.Class.define("osparc.store.Groups", { delete this.getOrganizations()[groupId]; }, - __addToUsersCache: function(user, orgId = null) { + __addMemberToCache: function(orgMember, orgId = null) { + const userMember = new osparc.data.model.UserMember(orgMember); if (orgId) { const organization = this.getOrganization(orgId); if (organization) { - organization.addGroupMember(user); + organization.addGroupMember(userMember); } } - this.getReachableUsers()[user.getGroupId()] = user; + osparc.store.Users.getInstance().addUser(orgMember); }, __removeUserFromCache: function(userId, orgId) { diff --git a/services/static-webserver/client/source/class/osparc/store/Users.js b/services/static-webserver/client/source/class/osparc/store/Users.js new file mode 100644 index 00000000000..59da1a2fb9a --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/store/Users.js @@ -0,0 +1,77 @@ +/* ************************************************************************ + + 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.store.Users", { + extend: qx.core.Object, + type: "singleton", + + properties: { + users: { + check: "Array", + init: [], + nullable: false, + }, + }, + + members: { + fetchUser: function(groupId) { + const params = { + url: { + gid: groupId + } + }; + return osparc.data.Resources.fetch("users", "get", params) + .then(userData => { + const user = this.addUser(userData[0]); + return user; + }); + }, + + getUser: function(groupId, fetchIfNotFound = true) { + const userFound = this.getUsers().find(user => user.getGroupId() === groupId); + if (userFound) { + return new Promise(resolve => resolve(userFound)); + } else if (fetchIfNotFound) { + return this.fetchUser(groupId); + } + return new Promise(reject => reject()); + }, + + addUser: function(userData) { + const user = new osparc.data.model.User(userData); + const userFound = this.getUsers().find(usr => usr.getGroupId() === user.getGroupId()); + if (!userFound) { + this.getUsers().push(user); + } + return user; + }, + + searchUsers: function(text) { + const params = { + data: { + match: text + } + }; + return osparc.data.Resources.fetch("users", "search", params) + .then(usersData => { + const users = []; + usersData.forEach(userData => users.push(this.addUser(userData))); + return users; + }); + }, + } +}); From 46594737172249aed72e12120ac6635aa209bda0 Mon Sep 17 00:00:00 2001 From: Sylvain <35365065+sanderegg@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:06:45 +0100 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20Database:=20added=20comp=5Ft?= =?UTF-8?q?asks=20submit=20column=20back=20for=20legacy=20services=20(?= =?UTF-8?q?=F0=9F=97=83=EF=B8=8F)=20(#7003)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7017ee1a49_add_deprecated_submit_column.py | 28 +++++++++++++++++++ .../models/comp_tasks.py | 12 ++++++-- .../models/comp_tasks.py | 8 +++++- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 packages/postgres-database/src/simcore_postgres_database/migration/versions/307017ee1a49_add_deprecated_submit_column.py diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/307017ee1a49_add_deprecated_submit_column.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/307017ee1a49_add_deprecated_submit_column.py new file mode 100644 index 00000000000..a93d032b8e8 --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/307017ee1a49_add_deprecated_submit_column.py @@ -0,0 +1,28 @@ +"""add deprecated submit column + +Revision ID: 307017ee1a49 +Revises: 1e3c9c804fec +Create Date: 2025-01-06 12:53:51.604189+00:00 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '307017ee1a49' +down_revision = '1e3c9c804fec' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('comp_tasks', sa.Column('submit', sa.DateTime(timezone=True), server_default=sa.text("'1900-01-01T00:00:00Z'::timestamptz"), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('comp_tasks', 'submit') + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py b/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py index 8389495d9b6..096447f7366 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py @@ -1,6 +1,4 @@ -""" Computational Tasks Table - -""" +"""Computational Tasks Table""" import enum @@ -102,6 +100,14 @@ class NodeClass(enum.Enum): nullable=True, doc="Harware information of this task", ), + # deprecated columns must be kept due to legacy services + # utc timestamps for submission/start/end + sa.Column( + "submit", + sa.DateTime(timezone=True), + server_default=sa.text("'1900-01-01T00:00:00Z'::timestamptz"), + doc="[DEPRECATED unused but kept for legacy services and must be filled with a default value of 1 January 1900]", + ), # ------ sa.UniqueConstraint("project_id", "node_id", name="project_node_uniqueness"), ) diff --git a/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py b/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py index 2b646cf424e..5a6675e5f6f 100644 --- a/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py +++ b/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py @@ -150,6 +150,10 @@ class CompTaskAtDB(BaseModel): pricing_info: dict | None hardware_info: HardwareInfo + submit: dt.datetime | None = Field( + default=None, deprecated=True, description="Required for legacy services" + ) + @field_validator("state", mode="before") @classmethod def _convert_state_from_state_type_enum_if_needed(cls, v): @@ -238,7 +242,9 @@ def to_db_model(self, **exclusion_rules) -> dict[str, Any]: "pricing_unit_id": 1, "pricing_unit_cost_id": 1, }, - "hardware_info": next(iter(HardwareInfo.model_config["json_schema_extra"]["examples"])), # type: ignore + "hardware_info": next( + iter(HardwareInfo.model_config["json_schema_extra"]["examples"]) # type: ignore + ), } for image_example in Image.model_config["json_schema_extra"]["examples"] # type: ignore ] From 208de24f68dd50e9f324f44f49543be1f7b1846c Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:37:52 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20[Frontend]=20bugfix:=20open?= =?UTF-8?q?=20folder=20after=20deleting=20file=20(#6997)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/class/osparc/file/FilesTree.js | 8 ++-- .../class/osparc/file/TreeFolderView.js | 11 +++-- .../class/osparc/widget/NodeDataManager.js | 4 ++ .../class/osparc/workbench/WorkbenchUI.js | 42 +++++++++---------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/file/FilesTree.js b/services/static-webserver/client/source/class/osparc/file/FilesTree.js index e2736c0fadd..a682bbca1c2 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilesTree.js +++ b/services/static-webserver/client/source/class/osparc/file/FilesTree.js @@ -181,7 +181,7 @@ qx.Class.define("osparc.file.FilesTree", { newChildren[0].children[0].children.length) { // node const nodeData = newChildren[0].children[0].children[0]; const nodeTreeName = nodeData.label; - this.__resetTree(nodeTreeName); + this.__resetTree(nodeTreeName, nodeId); const rootNodeModel = this.getModel(); if (nodeData.children.length) { const nodeItemsOnly = nodeData.children; @@ -253,12 +253,12 @@ qx.Class.define("osparc.file.FilesTree", { } }, - __resetTree: function(treeName) { - // FIXME: It is not resetting the model + __resetTree: function(treeName, itemId) { + itemId = itemId || treeName.replace(/\s/g, ""); // default to tree name without white spaces this.resetModel(); const rootData = { label: treeName, - itemId: treeName.replace(/\s/g, ""), // remove all white spaces + itemId, location: null, path: null, pathLabel: [treeName], 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 d9349713218..d85836d5ed7 100644 --- a/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js +++ b/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js @@ -119,15 +119,14 @@ qx.Class.define("osparc.file.TreeFolderView", { let found = false; while (!found && path.length) { found = foldersTree.findItemId(path.join("/")); - if (found) { - foldersTree.openNodeAndParents(found); - foldersTree.setSelection(new qx.data.Array([found])); - foldersTree.fireEvent("selectionChanged"); - } // look for next parent path.pop(); } - if (!found) { + if (found) { + foldersTree.openNodeAndParents(found); + foldersTree.setSelection(new qx.data.Array([found])); + foldersTree.fireEvent("selectionChanged"); + } else { folderViewer.resetFolder(); } } 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 2785f658c6c..397477d1d94 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js +++ b/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js @@ -125,6 +125,10 @@ qx.Class.define("osparc.widget.NodeDataManager", { foldersTree.resetCache(); const openSameFolder = () => { + if (!this.getStudyId()) { + // drop first, which is the study id + path.shift(); + } // drop last, which is the file path.pop(); treeFolderView.openPath(path); diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js index bf42bde89f7..37279ed42c1 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js @@ -129,7 +129,7 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { __selectedItemId: null, __startHint: null, __toolHint: null, - __dropHereNodeUI: null, + __dropHereUI: null, __selectionRectInitPos: null, __selectionRectRepr: null, __panning: null, @@ -1685,7 +1685,7 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { this.setDroppable(true); const stopDragging = e => { this.__isDraggingLink = null; - this.__updateWidgets(false); + this.__updateDropHere(false); }; const startDragging = e => { this.addListenerOnce("dragleave", stopDragging, this); @@ -1786,7 +1786,7 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { const posX = e.offsetX + 2; const posY = e.offsetY + 2; - this.__updateWidgets(dragging, posX, posY); + this.__updateDropHere(dragging, posX, posY); }, __draggingLink: function(e, dragging) { @@ -1802,14 +1802,14 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { } const pos = this.__pointerEventToWorkbenchPos(e); - this.__updateWidgets(dragging, pos.x, pos.y); + this.__updateDropHere(dragging, pos.x, pos.y); }, - __updateWidgets: function(dragging, posX, posY) { + __updateDropHere: function(show, posX, posY) { const boxWidth = 120; const boxHeight = 60; - if (this.__dropHereNodeUI === null) { - const dropHereNodeUI = this.__dropHereNodeUI = new qx.ui.basic.Label(this.tr("Drop here")).set({ + if (this.__dropHereUI === null) { + const dropHereNodeUI = this.__dropHereUI = new qx.ui.basic.Label(this.tr("Drop here")).set({ font: "workbench-start-hint", textColor: "workbench-start-hint" }); @@ -1817,19 +1817,21 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { this.__workbenchLayout.add(dropHereNodeUI); dropHereNodeUI.rect = this.__svgLayer.drawDashedRect(boxWidth, boxHeight); } - const dropMe = this.__dropHereNodeUI; - if (dragging) { - dropMe.show(); - const dropMeBounds = dropMe.getBounds() || dropMe.getSizeHint(); - dropMe.setLayoutProperties({ + let dropHere = this.__dropHereUI; + if (show) { + dropHere.show(); + const dropMeBounds = dropHere.getBounds() || dropHere.getSizeHint(); + dropHere.setLayoutProperties({ left: posX - parseInt(dropMeBounds.width/2) - parseInt(boxWidth/2), top: posY - parseInt(dropMeBounds.height/2)- parseInt(boxHeight/2) }); - if ("rect" in dropMe) { - osparc.wrapper.Svg.updateItemPos(dropMe.rect, posX - boxWidth, posY - boxHeight); + if ("rect" in dropHere) { + osparc.wrapper.Svg.updateItemPos(dropHere.rect, posX - boxWidth, posY - boxHeight); } } else { - this.__removeDropHint(); + dropHere.exclude(); + osparc.wrapper.Svg.removeItem(dropHere.rect); + dropHere = null; } }, @@ -2017,14 +2019,14 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { this.__draggingLink(e, false); if (this.__isDraggingLink && "dragData" in this.__isDraggingLink) { + const data = this.__isDraggingLink["dragData"]; + this.__isDraggingLink = null; const pos = this.__pointerEventToWorkbenchPos(e, false); const service = qx.data.marshal.Json.createModel(osparc.service.Utils.getFilePicker()); const nodeUI = await this.__addNode(service, pos); if (nodeUI) { const node = nodeUI.getNode(); - const data = this.__isDraggingLink["dragData"]; osparc.file.FilePicker.setOutputValueFromStore(node, data.getLocation(), data.getDatasetId(), data.getFileId(), data.getLabel()); - this.__isDraggingLink = null; } } }, @@ -2047,11 +2049,5 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { }); } }, - - __removeDropHint: function() { - this.__dropHereNodeUI.setVisibility("excluded"); - osparc.wrapper.Svg.removeItem(this.__dropHereNodeUI.rect); - this.__dropHereNodeUI = null; - } } });