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 8c031524cc3..ca433ad3712 100644 --- a/services/static-webserver/client/source/class/osparc/data/Roles.js +++ b/services/static-webserver/client/source/class/osparc/data/Roles.js @@ -138,7 +138,7 @@ qx.Class.define("osparc.data.Roles", { return this.__createIntoFromRoles(osparc.data.Roles.ORG); }, - createRolesWalleltInfo: function() { + createRolesWalletInfo: function() { return this.__createIntoFromRoles(osparc.data.Roles.WALLET); }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js index 740410ba979..5603c1c6fbf 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js @@ -69,9 +69,8 @@ qx.Class.define("osparc.desktop.MainPage", { const preferenceSettings = osparc.Preferences.getInstance(); const preferenceWalletId = preferenceSettings.getPreferredWalletId(); const wallets = store.getWallets(); - if (preferenceWalletId === null && wallets && wallets.length) { - // Select one by default: according to the use case, the one larger number of accessRights - wallets.sort((a, b) => b.getAccessRights().length - a.getAccessRights().length); + if (wallets.length === 1 && (preferenceWalletId === null || osparc.desktop.credits.Utils.getWallet(preferenceWalletId) === null)) { + // If there is only one wallet available, make it default preferenceSettings.requestChangePreferredWalletId(wallets[0].getWalletId()); } }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/Overview.js b/services/static-webserver/client/source/class/osparc/desktop/credits/Overview.js index cb7eb52e537..9e02e2d847f 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/Overview.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/Overview.js @@ -115,7 +115,7 @@ qx.Class.define("osparc.desktop.credits.Overview", { __createWalletsView: function() { const activeWallet = osparc.store.Store.getInstance().getActiveWallet(); - const preferredWallet = osparc.desktop.credits.Utils.getFavouriteWallet(); + const preferredWallet = osparc.desktop.credits.Utils.getPreferredWallet(); const oneWallet = activeWallet ? activeWallet : preferredWallet; if (oneWallet) { // show one wallet diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenter.js b/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenter.js index e938845d8a9..cec6b97bafc 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenter.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenter.js @@ -251,25 +251,27 @@ qx.Class.define("osparc.desktop.credits.UserCenter", { __openPage: function(page) { if (page) { this.__tabsView.setSelection([page]); + return true; } + return false; }, openOverview: function() { if (this.__overviewPage) { - this.__openPage(this.__overviewPage); - } else { - // fallback - this.__openPage(this.__profilePage); + return this.__openPage(this.__overviewPage); } + // fallback + this.__openPage(this.__profilePage); + return false; }, openWallets: function() { if (this.__walletsPage) { - this.__openPage(this.__walletsPage); - } else { - // fallback - this.__openPage(this.__profilePage); + return this.__openPage(this.__walletsPage); } + // fallback + this.__openPage(this.__profilePage); + return false; }, __openBuyCredits: function() { diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenterWindow.js b/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenterWindow.js index 5566915f81d..6079d06ae3f 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenterWindow.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/UserCenterWindow.js @@ -52,11 +52,11 @@ qx.Class.define("osparc.desktop.credits.UserCenterWindow", { __userCenter: null, openOverview: function() { - this.__userCenter.openOverview(); + return this.__userCenter.openOverview(); }, openWallets: function() { - this.__userCenter.openWallets(); + return this.__userCenter.openWallets(); } } }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/Utils.js b/services/static-webserver/client/source/class/osparc/desktop/credits/Utils.js index e1ac051998e..c8b0ed9429b 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/Utils.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/Utils.js @@ -88,14 +88,14 @@ qx.Class.define("osparc.desktop.credits.Utils", { getWallet: function(walletId) { const store = osparc.store.Store.getInstance(); const wallets = store.getWallets(); - const favouriteWallet = wallets.find(wallet => wallet.getWalletId() === walletId); - if (favouriteWallet) { - return favouriteWallet; + const foundWallet = wallets.find(wallet => wallet.getWalletId() === walletId); + if (foundWallet) { + return foundWallet; } return null; }, - getFavouriteWallet: function() { + getPreferredWallet: function() { const store = osparc.store.Store.getInstance(); const wallets = store.getWallets(); const favouriteWallet = wallets.find(wallet => wallet.isPreferredWallet()); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/WalletsMiniViewer.js b/services/static-webserver/client/source/class/osparc/desktop/credits/WalletsMiniViewer.js index 456d3d97b6c..3078b892946 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/WalletsMiniViewer.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/WalletsMiniViewer.js @@ -46,14 +46,6 @@ qx.Class.define("osparc.desktop.credits.WalletsMiniViewer", { this.__walletListeners = []; this.__buildLayout(); - - this.addListener("tap", () => { - osparc.desktop.credits.Utils.areWalletsEnabled() - .then(walletsEnabled => { - const creditsWindow = osparc.desktop.credits.UserCenterWindow.openWindow(walletsEnabled); - creditsWindow.openOverview(); - }); - }, this); }, properties: { @@ -76,15 +68,22 @@ qx.Class.define("osparc.desktop.credits.WalletsMiniViewer", { __reloadLayout: function() { const activeWallet = this.getActiveWallet(); - const preferredWallet = osparc.desktop.credits.Utils.getFavouriteWallet(); + const preferredWallet = osparc.desktop.credits.Utils.getPreferredWallet(); const oneWallet = activeWallet ? activeWallet : preferredWallet; if (oneWallet) { this.__showOneWallet(oneWallet); - } else if (osparc.store.Store.getInstance().getWallets().length) { - this.__showAllWallets(); } else { - this.__showNoWallets(); + this.__showSelectWallet(); } + + const store = osparc.store.Store.getInstance(); + store.getWallets().forEach(wallet => { + const preferredWalletId = wallet.addListener("changePreferredWallet", () => this.__reloadLayout()); + this.__walletListeners.push({ + walletId: wallet.getWalletId(), + listenerId: preferredWalletId + }); + }); }, __removeWallets: function() { @@ -99,69 +98,34 @@ qx.Class.define("osparc.desktop.credits.WalletsMiniViewer", { this._removeAll(); }, - __showNoWallets: function() { + __showSelectWallet: function() { this.__removeWallets(); - this._add(new qx.ui.core.Spacer(), { - flex: 1 - }); - const iconSrc = "@MaterialIcons/account_balance_wallet/26"; - const walletsButton = new qx.ui.form.Button(null, iconSrc).set({ - toolTipText: this.tr("No Wallets"), - backgroundColor: "transparent", + const walletsButton = new qx.ui.basic.Image(iconSrc).set({ + toolTipText: this.tr("Select Wallet"), textColor: "danger-red" }); walletsButton.addListener("tap", () => { osparc.desktop.credits.Utils.areWalletsEnabled() .then(walletsEnabled => { - const creditsWindow = osparc.desktop.credits.UserCenterWindow.openWindow(walletsEnabled); - creditsWindow.openWallets(); + const userCenterWindow = osparc.desktop.credits.UserCenterWindow.openWindow(walletsEnabled); + userCenterWindow.openWallets(); }); }, this); this._add(walletsButton, { flex: 1 }); - - this._add(new qx.ui.core.Spacer(), { - flex: 1 - }); }, __showOneWallet: function(wallet) { this.__removeWallets(); - this._add(new qx.ui.core.Spacer(), { - flex: 1 - }); - this.__addWallet(wallet); - this.__addWalletListener(wallet); - - this._add(new qx.ui.core.Spacer(), { - flex: 1 - }); - }, - - __showAllWallets: function() { - this.__removeWallets(); - - this._add(new qx.ui.core.Spacer(), { - flex: 1 - }); - - const wallets = osparc.store.Store.getInstance().getWallets(); - const maxIndicators = 3; - for (let i=0; i this.__reloadLayout()); + this.__walletListeners.push({ + walletId: wallet.getWalletId(), + listenerId: changeStatusId }); }, @@ -169,23 +133,16 @@ qx.Class.define("osparc.desktop.credits.WalletsMiniViewer", { const creditsLabel = new osparc.desktop.credits.CreditsLabel(wallet, true).set({ alignX: "right" }); + creditsLabel.addListener("tap", () => { + osparc.desktop.credits.Utils.areWalletsEnabled() + .then(walletsEnabled => { + const creditsWindow = osparc.desktop.credits.UserCenterWindow.openWindow(walletsEnabled); + creditsWindow.openOverview(); + }); + }, this); this._add(creditsLabel, { flex: 1 }); - }, - - __addWalletListener: function(wallet) { - const changeStatusId = wallet.addListener("changeStatus", () => this.__reloadLayout()); - this.__walletListeners.push({ - walletId: wallet.getWalletId(), - listenerId: changeStatusId - }); - - const preferredWalletId = wallet.addListener("changePreferredWallet", () => this.__reloadLayout()); - this.__walletListeners.push({ - walletId: wallet.getWalletId(), - listenerId: preferredWalletId - }); } } }); 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 f22d45b5bb3..ede289ddc36 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 @@ -97,9 +97,9 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { __canIWrite: function() { const wallet = this.__currentModel; if (wallet) { - const myAcessRights = wallet.getMyAccessRights(); - if (myAcessRights) { - return myAcessRights["write"]; + const myAccessRights = wallet.getMyAccessRights(); + if (myAccessRights) { + return myAccessRights["write"]; } } return false; @@ -140,7 +140,7 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { }, __getRolesToolbar: function() { - return osparc.data.Roles.createRolesWalleltInfo(); + return osparc.data.Roles.createRolesWalletInfo(); }, __getMembersFilter: function() { @@ -152,7 +152,7 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { }, __getMembersList: function() { - const memebersUIList = new qx.ui.form.List().set({ + const membersUIList = new qx.ui.form.List().set({ decorator: "no-border", spacing: 3, width: 150, @@ -160,7 +160,7 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { }); const membersModel = this.__membersModel = new qx.data.Array(); - const membersCtrl = new qx.data.controller.List(membersModel, memebersUIList, "name"); + const membersCtrl = new qx.data.controller.List(membersModel, membersUIList, "name"); membersCtrl.setDelegate({ createItem: () => new osparc.desktop.wallets.MemberListItem(), bindItem: (ctrl, item, id) => { @@ -195,7 +195,7 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { } }); - return memebersUIList; + return membersUIList; }, __reloadWalletMembers: async function() { @@ -276,6 +276,19 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { cb(); } }); + + // push 'WALLET_SHARED' notification + osparc.store.Store.getInstance().getPotentialCollaborators() + .then(potentialCollaborators => { + gids.forEach(gid => { + if (gid in potentialCollaborators && "id" in potentialCollaborators[gid]) { + // it's a user, not an organization + const collab = potentialCollaborators[gid]; + const uid = collab["id"]; + osparc.notification.Notifications.postNewWallet(uid, wallet.getWalletId()); + } + }); + }); }); }, diff --git a/services/static-webserver/client/source/class/osparc/notification/Notification.js b/services/static-webserver/client/source/class/osparc/notification/Notification.js index f4d7d3cbc02..37f102e466f 100644 --- a/services/static-webserver/client/source/class/osparc/notification/Notification.js +++ b/services/static-webserver/client/source/class/osparc/notification/Notification.js @@ -45,7 +45,8 @@ qx.Class.define("osparc.notification.Notification", { "NEW_ORGANIZATION", "STUDY_SHARED", "TEMPLATE_SHARED", - "ANNOTATION_NOTE" + "ANNOTATION_NOTE", + "WALLET_SHARED" ], init: null, nullable: false, diff --git a/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js b/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js index 993b4e4b5ea..8e89239d998 100644 --- a/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js +++ b/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js @@ -130,6 +130,9 @@ qx.Class.define("osparc.notification.NotificationUI", { case "ANNOTATION_NOTE": source = "@FontAwesome5Solid/file/14"; break; + case "WALLET_SHARED": + source = "@MaterialIcons/account_balance_wallet/14"; + break; } return source; } @@ -181,51 +184,87 @@ qx.Class.define("osparc.notification.NotificationUI", { // open actionable path const actionablePath = notification.getActionablePath(); + const items = actionablePath.split("/"); + const resourceId = items.pop(); const category = notification.getCategory(); switch (category) { - case "NEW_ORGANIZATION": { - const items = actionablePath.split("/"); - const orgId = items.pop(); - // make sure org is available - osparc.store.Store.getInstance().getGroup(orgId) - .then(org => { - if (org) { - const orgsWindow = osparc.desktop.organizations.OrganizationsWindow.openWindow(); - orgsWindow.openOrganizationDetails(parseInt(orgId)); - } else { - const msg = this.tr("You don't have access anymore"); - osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); - } - }); + case "NEW_ORGANIZATION": + this.__openOrganizationDetails(parseInt(resourceId)); break; - } case "TEMPLATE_SHARED": case "STUDY_SHARED": - case "ANNOTATION_NOTE": { - const items = actionablePath.split("/"); - const studyId = items.pop(); - const params = { - url: { - "studyId": studyId - } - }; - osparc.data.Resources.getOne("studies", params) - .then(studyData => { - if (studyData) { - const studyDataCopy = osparc.data.model.Study.deepCloneStudyObject(studyData); - studyDataCopy["resourceType"] = notification.getCategory() === "TEMPLATE_SHARED" ? "template" : "study"; - const moreOpts = new osparc.dashboard.ResourceMoreOptions(studyDataCopy); - const win = osparc.dashboard.ResourceMoreOptions.popUpInWindow(moreOpts); - moreOpts.addListener("openingStudy", () => win.close()); + case "ANNOTATION_NOTE": + this.__openStudyDetails(resourceId, notification); + break; + case "WALLET_SHARED": + osparc.desktop.credits.Utils.areWalletsEnabled() + .then(walletsEnabled => { + if (walletsEnabled) { + this.__openWalletDetails(parseInt(resourceId)); } - }) - .catch(err => { - console.error(err); - const msg = this.tr("You don't have access anymore"); - osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); }); break; + } + }, + + __openOrganizationDetails: function(orgId) { + // make sure org is available + osparc.store.Store.getInstance().getGroup(orgId) + .then(org => { + if (org) { + const orgsWindow = osparc.desktop.organizations.OrganizationsWindow.openWindow(); + orgsWindow.openOrganizationDetails(orgId); + } else { + const msg = this.tr("You don't have access anymore"); + osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); + } + }); + }, + + __openStudyDetails: function(studyId, notification) { + const params = { + url: { + "studyId": studyId + } + }; + osparc.data.Resources.getOne("studies", params) + .then(studyData => { + if (studyData) { + const studyDataCopy = osparc.data.model.Study.deepCloneStudyObject(studyData); + studyDataCopy["resourceType"] = notification.getCategory() === "TEMPLATE_SHARED" ? "template" : "study"; + const moreOpts = new osparc.dashboard.ResourceMoreOptions(studyDataCopy); + const win = osparc.dashboard.ResourceMoreOptions.popUpInWindow(moreOpts); + moreOpts.addListener("openingStudy", () => win.close()); + } + }) + .catch(err => { + console.error(err); + const msg = this.tr("You don't have access anymore"); + osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); + }); + }, + + __openWalletDetails: function(walletId) { + const wallet = osparc.desktop.credits.Utils.getWallet(walletId); + if (wallet) { + const userCenterWindow = osparc.desktop.credits.UserCenterWindow.openWindow(true); + if (userCenterWindow.openWallets()) { + const msg = this.tr("Do you want to make it the default Credit Account?"); + const win = new osparc.ui.window.Confirmation(msg).set({ + confirmAction: "create" + }); + win.center(); + win.open(); + win.addListener("close", () => { + if (win.getConfirmed()) { + const preferenceSettings = osparc.Preferences.getInstance(); + preferenceSettings.requestChangePreferredWalletId(walletId); + } + }, this); } + } else { + const msg = this.tr("You don't have access anymore"); + osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); } } } diff --git a/services/static-webserver/client/source/class/osparc/notification/Notifications.js b/services/static-webserver/client/source/class/osparc/notification/Notifications.js index 1fe04e9a719..7c0a80a094f 100644 --- a/services/static-webserver/client/source/class/osparc/notification/Notifications.js +++ b/services/static-webserver/client/source/class/osparc/notification/Notifications.js @@ -75,6 +75,17 @@ qx.Class.define("osparc.notification.Notifications", { }; }, + __newWalletObj: function(userId, walletId) { + return { + "user_id": userId.toString(), + "category": "WALLET_SHARED", + "actionable_path": "wallet/"+walletId, + "title": "Credits shared", + "text": "A Credit account was shared with you", + "date": new Date().toISOString() + }; + }, + postNewOrganization: function(userId, orgId) { const params = { data: this.__newOrganizationObj(userId, orgId) @@ -101,6 +112,13 @@ qx.Class.define("osparc.notification.Notifications", { data: this.__newAnnotationNoteObj(userId, studyId) }; return osparc.data.Resources.fetch("notifications", "post", params); + }, + + postNewWallet: function(userId, studyId) { + const params = { + data: this.__newWalletObj(userId, studyId) + }; + return osparc.data.Resources.fetch("notifications", "post", params); } }, diff --git a/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js b/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js index ad8f5612178..b862609a43a 100644 --- a/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js +++ b/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js @@ -248,9 +248,9 @@ qx.Class.define("osparc.share.CollaboratorsStudy", { }); }, - __make: function(collboratorGId, newAccessRights, successMsg, failureMsg, item) { + __make: function(collaboratorGId, newAccessRights, successMsg, failureMsg, item) { item.setEnabled(false); - this._serializedData["accessRights"][collboratorGId] = newAccessRights; + this._serializedData["accessRights"][collaboratorGId] = newAccessRights; const params = { url: { "studyId": this._serializedData["uuid"] diff --git a/services/static-webserver/client/source/class/osparc/study/ResourceSelector.js b/services/static-webserver/client/source/class/osparc/study/ResourceSelector.js index a3abab4457d..749e1bbdd8d 100644 --- a/services/static-webserver/client/source/class/osparc/study/ResourceSelector.js +++ b/services/static-webserver/client/source/class/osparc/study/ResourceSelector.js @@ -367,7 +367,7 @@ qx.Class.define("osparc.study.ResourceSelector", { const selection = e.getData(); selectWallet(selection[0].walletId); }); - const favWallet = osparc.desktop.credits.Utils.getFavouriteWallet(); + const favWallet = osparc.desktop.credits.Utils.getPreferredWallet(); if (this.__projectWalletId) { selectWallet(this.__projectWalletId); } else if (favWallet) { diff --git a/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js b/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js index 8e099be7c28..c5048817be1 100644 --- a/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js +++ b/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js @@ -26,6 +26,7 @@ qx.Class.define("osparc.ui.window.Confirmation", { this.addCancelButton(); const confirmButton = this.__confirmButton = new qx.ui.form.Button(); + this.bind("confirmText", confirmButton, "label"); confirmButton.addListener("execute", () => { this.setConfirmed(true); this.close(1); @@ -39,7 +40,7 @@ qx.Class.define("osparc.ui.window.Confirmation", { confirmText: { check: "String", init: "Yes", - apply: "__applyConfirmText" + event: "changeConfirmText" }, confirmAction: { @@ -66,10 +67,6 @@ qx.Class.define("osparc.ui.window.Confirmation", { return this.getChildControl("cancel-button"); }, - __applyConfirmText: function(confirmText) { - this.__confirmButton.setLabel(confirmText); - }, - __applyConfirmAppearance: function(confirmationAction) { const confBtn = this.__confirmButton; switch (confirmationAction) { diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications.py b/services/web/server/src/simcore_service_webserver/users/_notifications.py index e9498d8d8fe..b1b02507822 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications.py @@ -20,6 +20,7 @@ class NotificationCategory(StrAutoEnum): STUDY_SHARED = auto() TEMPLATE_SHARED = auto() ANNOTATION_NOTE = auto() + WALLET_SHARED = auto() class BaseUserNotification(BaseModel): @@ -100,5 +101,15 @@ class Config: "date": "2023-02-23T16:28:13.122Z", "read": False, }, + { + "id": "390053c9-3931-40e1-839f-585268f6fd3e", + "user_id": "1", + "category": "WALLET_SHARED", + "actionable_path": "wallet/21", + "title": "Credits shared", + "text": "A Credit account was shared with you", + "date": "2023-09-29T16:28:13.122Z", + "read": False, + }, ] }