From 355dc96c6d5dbb6f3151bcbdec9e6a8de49ba3f8 Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Mon, 12 May 2025 13:15:34 +0300 Subject: [PATCH 01/83] Update sonar.yml --- .github/workflows/sonar.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index eb0506080..4548716a0 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -1,18 +1,23 @@ -on: +name: Sonar Analysis Workflow + +on: push: branches: - master - development + pull_request: + types: [opened, synchronize, reopened] -name: Sonar Analysis Workflow jobs: - sonarQubeTrigger: - name: SonarQube Trigger + build: + name: Build and analyze runs-on: ubuntu-latest + steps: - - uses: actions/checkout@master - - name: SonarQube Scan - uses: kitabisa/sonarqube-action@master - with: - host: ${{ secrets.SONAR_HOST }} - login: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} From 8e16b8359da4cb11e658421e081899fecf912904 Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Mon, 12 May 2025 13:16:57 +0300 Subject: [PATCH 02/83] Update sonar-project.properties --- sonar-project.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index b23243692..a6c9d5714 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,4 +1,4 @@ -sonar.projectKey=marketplace-dapp -sonar.projectName=marketplace-dapp +sonar.projectKey=singnet_snet-dapp_df2bdd78-17f8-467d-a908-74e4c0758f08 +sonar.projectName=snet-dapp sonar.projectVersion=0.0.1 -sonar.sources=. \ No newline at end of file +sonar.sources=. From 46a1e129116790ef1cd9b8b0ff7a16912f9b547f Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 12 May 2025 16:25:16 +0300 Subject: [PATCH 03/83] [SM-68] add link to marketplace stand by tokens --- .../Header/NavigateToMainPortalButton.js | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/components/common/Header/NavigateToMainPortalButton.js b/src/components/common/Header/NavigateToMainPortalButton.js index 9d0e516b1..f5d5950f6 100644 --- a/src/components/common/Header/NavigateToMainPortalButton.js +++ b/src/components/common/Header/NavigateToMainPortalButton.js @@ -1,19 +1,42 @@ import { isMainnet } from "../../../config/Networks"; import StyledButton from "../StyledButton"; +// Note: Buttons intentionally link to opposite marketplaces +const MARKETPLACE_LINKS = { + TESTNET: { + label: "Marketplace Mainnet", + link: "https://marketplace.singularitynet.io/", + }, + FET: { + label: "AGIX Marketplace", + link: "https://agix-marketplace.singularitynet.io/", + }, + AGIX: { + label: "FET Marketplace", + link: "https://marketplace.singularitynet.io/", + }, +}; + +const CURRENT_NETWORK = process.env?.REACT_APP_ETH_NETWORK; +const CURRENT_TOKEN = process.env?.REACT_APP_TOKEN_NAME; + const NavigateToMainPortalButton = () => { - if (isMainnet(process.env.REACT_APP_ETH_NETWORK)) { - return null; - } else { - return ( - - ); + if (!CURRENT_NETWORK || !CURRENT_TOKEN) { + throw new Error("Network ID or Current Token wasn't provided"); } + + const headerMarketplaceLink = isMainnet(CURRENT_NETWORK) + ? MARKETPLACE_LINKS[CURRENT_TOKEN] + : MARKETPLACE_LINKS.TESTNET; + + return ( + + ); }; export default NavigateToMainPortalButton; From aee8341980a421c01e97421a7d2015bde248b3df Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 14 May 2025 13:44:11 +0300 Subject: [PATCH 04/83] [SM-82] add handle of login error for users with the uncofirmed mail --- src/Redux/actionCreators/UserActions.js | 61 +++++++++++++++---------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index 79755d3e6..9be4e776c 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -43,6 +43,10 @@ export const walletTypes = { DEFAULT: "default", }; +const NEXT_SIGN_IN_STEP = { + CONFIRM_SIGN_UP: "CONFIRM_SIGN_UP", +}; + const setJWTExp = (exp) => ({ type: SET_JWT_EXP, payload: exp }); export const fetchAuthenticatedUser = () => async (dispatch) => { @@ -279,36 +283,43 @@ export const login = async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.LOGIN)); let userDetails = {}; - return signIn({ username: email, password }) - .then(() => { + try { + const loginRequest = await signIn({ username: email, password }); + + if (loginRequest?.isSignedIn) { dispatch(loginSuccess({ route })); - }) - .catch((err) => { - if (err.code === "PasswordResetRequiredException") { - dispatch(updateEmail(email)); - History.navigate(`/${Routes.RESET_PASSWORD}`); - dispatch(loaderActions.stopAppLoader()); - return; - } else if (err.code === "UserNotConfirmedException") { - dispatch(updateEmail(email)); - userDetails = { - type: LOGIN_SUCCESS, - payload: { login: { isLoggedIn: true } }, - }; - dispatch(userDetails); - History.navigate(`/${Routes.ONBOARDING}`); - dispatch(loaderActions.stopAppLoader()); - return; - } - const error = parseError(err); + } + if (loginRequest?.nextStep?.signInStep === NEXT_SIGN_IN_STEP.CONFIRM_SIGN_UP) { + throw new Error("User does not exist."); + } + throw new Error("Something went wrong. Please, try again later"); + } catch (err) { + if (err?.code === "PasswordResetRequiredException") { + dispatch(updateEmail(email)); + History.navigate(`/${Routes.RESET_PASSWORD}`); + dispatch(loaderActions.stopAppLoader()); + return; + } else if (err?.code === "UserNotConfirmedException") { + dispatch(updateEmail(email)); userDetails = { - type: LOGIN_ERROR, - payload: { login: { error } }, + type: LOGIN_SUCCESS, + payload: { login: { isLoggedIn: true } }, }; dispatch(userDetails); + History.navigate(`/${Routes.ONBOARDING}`); dispatch(loaderActions.stopAppLoader()); - throw err; - }); + return; + } + const error = parseError(err); + userDetails = { + type: LOGIN_ERROR, + payload: { login: { error } }, + }; + dispatch(userDetails); + throw err; + } finally { + dispatch(loaderActions.stopAppLoader()); + } }; const registrationAPI = (token) => { From 44e0a06c21f28dac128a6f62abe12338d89e0e48 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 14 May 2025 16:29:01 +0300 Subject: [PATCH 05/83] [SM-82] add handle of login error --- src/components/Login/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Login/index.js b/src/components/Login/index.js index f8a396be6..4a6ef8980 100644 --- a/src/components/Login/index.js +++ b/src/components/Login/index.js @@ -51,7 +51,11 @@ const Login = ({ classes }) => { } event.preventDefault(); event.stopPropagation(); - await dispatch(userActions.login({ email, password, route })); + try { + await dispatch(userActions.login({ email, password, route })); + } catch (err) { + console.error(err); + } }; return ( From 9384e8d0dec99d59df44b0dca62590d8f521a84f Mon Sep 17 00:00:00 2001 From: fmoloshnikov Date: Fri, 23 May 2025 16:25:53 +0300 Subject: [PATCH 06/83] [SM-95] fix Contributors field --- .../ServiceDetails/ProjectDetails/Contibutors.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/ServiceDetails/ProjectDetails/Contibutors.js b/src/components/ServiceDetails/ProjectDetails/Contibutors.js index 94ace201f..5a5a01340 100644 --- a/src/components/ServiceDetails/ProjectDetails/Contibutors.js +++ b/src/components/ServiceDetails/ProjectDetails/Contibutors.js @@ -1,18 +1,12 @@ -import React from "react"; import PropTypes from "prop-types"; import isEmpty from "lodash/isEmpty"; -import Row from "./Row"; const Contibutors = ({ contributors }) => { if (isEmpty(contributors)) { return null; } - const contributorsNames = contributors.map((contributor, index) => ( - - {contributor.name} {index + 1 !== contributors.length ? "," : ""} - - )); - return ; + const contributorsNames = contributors.map((contributor, index) => contributor.name).join(", "); + return contributorsNames; }; Contibutors.propTypes = { From e9b0d008f2177dae793b7adc913b24862a2ab368 Mon Sep 17 00:00:00 2001 From: fmoloshnikov Date: Fri, 23 May 2025 19:06:06 +0300 Subject: [PATCH 07/83] [SM-99] fix terms of service page on login --- src/Redux/actionCreators/UserActions.js | 8 ++++++-- src/Redux/reducers/UserReducer.js | 1 + src/components/Login/index.js | 4 +--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index 9be4e776c..5edceb678 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -259,7 +259,7 @@ const getCurrentUser = async () => { export const loginSuccess = ({ route }) => - async (dispatch) => { + async (dispatch, getState) => { const { userAttributes, idToken } = await getCurrentUser(); const userDetails = { @@ -273,8 +273,12 @@ export const loginSuccess = }; dispatch(userDetails); - History.navigate(route); await dispatch(fetchUserProfile(idToken.toString())); + const isTermsAccepted = getState().userReducer.isTermsAccepted; + if (!isTermsAccepted) { + route = `/${Routes.ONBOARDING}`; + } + History.navigate(route); dispatch(loaderActions.stopAppLoader()); }; diff --git a/src/Redux/reducers/UserReducer.js b/src/Redux/reducers/UserReducer.js index 06ed5ea49..c578c3d23 100644 --- a/src/Redux/reducers/UserReducer.js +++ b/src/Redux/reducers/UserReducer.js @@ -87,6 +87,7 @@ const userReducer = (state = InitialUserDetails, action) => { ...state.login, ...action.payload.login, }, + isTermsAccepted: action.payload.isTermsAccepted, wallet: { type: walletTypes.SNET }, }; } diff --git a/src/components/Login/index.js b/src/components/Login/index.js index 4a6ef8980..3b06a74f4 100644 --- a/src/components/Login/index.js +++ b/src/components/Login/index.js @@ -17,8 +17,6 @@ const Login = ({ classes }) => { const dispatch = useDispatch(); const location = useLocation(); const loginError = useSelector((state) => state.userReducer.login.error); - const isTermsAccepted = useSelector((state) => state.userReducer.isTermsAccepted); - const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); @@ -43,7 +41,7 @@ const Login = ({ classes }) => { }; const handleSubmit = async (event) => { - let route = isTermsAccepted ? getLinkToMarketplace() : `/${Routes.ONBOARDING}`; + const route = getLinkToMarketplace(); const isNotValid = snetValidator({ email, password }, loginConstraints); if (isNotValid) { dispatch(userActions.updateLoginError(isNotValid[0])); From 6e3acdea5221f2e55a8bc1e0cfb2ced5603ca852 Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Mon, 26 May 2025 13:58:01 +0300 Subject: [PATCH 08/83] Update sonar.yml --- .github/workflows/sonar.yml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index eb0506080..0669e954d 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -1,18 +1,24 @@ -on: + +name: Sonar Analysis Workflow + +on: push: branches: - master - development + pull_request: + types: [opened, synchronize, reopened] -name: Sonar Analysis Workflow jobs: - sonarQubeTrigger: - name: SonarQube Trigger + build: + name: Build and analyze runs-on: ubuntu-latest + steps: - - uses: actions/checkout@master - - name: SonarQube Scan - uses: kitabisa/sonarqube-action@master - with: - host: ${{ secrets.SONAR_HOST }} - login: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} From 1363a2f923b182ce4216a16135694b37a1de2a1a Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Mon, 26 May 2025 14:02:04 +0300 Subject: [PATCH 09/83] Update sonar-project.properties --- sonar-project.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index b23243692..a6c9d5714 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,4 +1,4 @@ -sonar.projectKey=marketplace-dapp -sonar.projectName=marketplace-dapp +sonar.projectKey=singnet_snet-dapp_df2bdd78-17f8-467d-a908-74e4c0758f08 +sonar.projectName=snet-dapp sonar.projectVersion=0.0.1 -sonar.sources=. \ No newline at end of file +sonar.sources=. From 38de9e12717261da790d1370bdfff076f4f2e230 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:41:10 +0300 Subject: [PATCH 10/83] [SM-103] remove unused files --- .../PaymentPopup/VerifyKey/index.js | 101 ----------------- .../PaymentPopup/VerifyKey/styles.js | 104 ------------------ 2 files changed, 205 deletions(-) delete mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/index.js delete mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/styles.js diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/index.js deleted file mode 100644 index 300daa23b..000000000 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/index.js +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState } from "react"; -import { withStyles } from "@mui/styles"; -import Typography from "@mui/material/Typography"; -import Web3 from "web3"; -import { useSelector } from "react-redux"; - -import AlertBox, { alertTypes } from "../../../../../../../../common/AlertBox"; -import StyledButton from "../../../../../../../../common/StyledButton"; -import BulletPoint from "../../../../../../../../common/BulletPoint"; -import { useStyles } from "./styles"; -import AlertText from "../../../../../../../../common/AlertText"; -import { isEmpty } from "lodash"; - -const warningMessage = [ - `You will still be able to top up your wallet and use it with all providers that you have already opened payment channels to.`, - `You won't be able to Link your wallet with any new providers`, -]; - -const web3 = new Web3(process.env.REACT_APP_WEB3_PROVIDER, null, {}); - -const VerifyKey = ({ classes, handleLostPrivateKey, handleUserProvidedPrivateKey, handleNextSection }) => { - const [keyLost, setKeyLost] = useState(false); - const [privateKey, setPrivateKey] = useState(""); - const [alert, setAlert] = useState({}); - const walletList = useSelector((state) => state.userReducer.walletList); - - const validatePrivateKey = (event) => { - event.preventDefault(); - try { - handleUserProvidedPrivateKey(privateKey); - const account = web3.eth.accounts.privateKeyToAccount(privateKey); - const GenWalletMatchingAcc = walletList.find((wallet) => wallet.address === account.address); - if (!GenWalletMatchingAcc) { - setAlert({ - type: alertTypes.ERROR, - message: "The private key doesn't belong to any of the General Account Wallets", - }); - return; - } - handleNextSection(); - } catch (error) { - console.error("error: ", error); - setAlert({ - type: alertTypes.ERROR, - message: "Please check the key you entered and try again.", - }); - return; - } - }; - - if (keyLost) { - return ( -
- - If you have lost your wallet private key, we can create a new wallet for you, though please take note that: - -
- {warningMessage.map((msg) => ( - - ))} -
- - Are you sure you want to go ahead and create a new wallet? - -
- setKeyLost(false)} /> - -
-
- ); - } - - return ( -
-

- Please enter the private key for your 'General Account Wallet' and we’ll create a payment channel - to the service provider. -

-
-
- Private Key - setPrivateKey(e.target.value)} /> - -
- -
- setKeyLost(true)} /> - -
- -
- ); -}; - -export default withStyles(useStyles)(VerifyKey); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/styles.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/styles.js deleted file mode 100644 index 9393a83b9..000000000 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/VerifyKey/styles.js +++ /dev/null @@ -1,104 +0,0 @@ -export const useStyles = (theme) => ({ - VerifyKeyContainer: { padding: "0 25px !important" }, - VerifyKeyDesc: { - margin: 0, - fontSize: 16, - color: "#2A2A2A", - lineHeight: "24px", - "& span": { - color: theme.palette.text.mediumShadeGray, - fontWeight: "600", - }, - "& a": { - color: theme.palette.text.primary, - textDecoration: "none", - }, - }, - textField: { - padding: "0 49px", - margin: "41px 0 32px", - position: "relative", - "& span": { - padding: 5, - marginLeft: 49, - position: "absolute", - top: -14, - left: 10, - backgroundColor: theme.palette.text.white, - color: theme.palette.text.black1, - fontSize: 12, - letterSpacing: 0.4, - lineHeight: "16px", - }, - "& input": { - width: "100%", - border: 1, - borderStyle: "solid", - borderColor: "rgba(25,25,25,0.32)", - borderRadius: 4, - padding: "19px 10px", - }, - "& + p": { margin: "0 49px 16px" }, - }, - error: { - "& span": { color: theme.palette.text.errorRed }, - "& input": { borderColor: theme.palette.text.errorRed }, - }, - errorMsg: { - padding: "5px 0 0 16px", - fontFamily: theme.typography.primary.main, - fontSize: 12, - color: theme.palette.text.errorRed, - letterSpacing: 0.4, - lineHeight: "16px", - }, - btnContainer: { - display: "flex", - justifyContent: "center", - gap: 30, - }, - lostKeyContainer: { paddingBottom: "0 !important" }, - WarningBoxConatiner: { - padding: "12px 17px", - borderWidth: 1, - borderStyle: "solid", - borderColor: "#F18D5A", - backgroundColor: "#FDF3E5", - borderRadius: 4, - margin: "32px 68px", - "& div": { - marginBottom: 17, - display: "flex", - alignItems: "flex-start", - "&:last-of-type": { marginBottom: 0 }, - }, - "& p": { - border: "none", - margin: "0 0 12px", - padding: 0, - display: "inline", - color: theme.palette.text.mediumShadeGray, - "&:last-of-type": { marginBottom: 0 }, - }, - "& svg": { - color: "#FFC200", - marginRight: 12, - verticalAlign: "middle", - }, - }, - lostKeyInfo: { - padding: "0 24px", - color: theme.palette.text.mediumShadeGray, - fontFamily: theme.typography.primary.main, - fontSize: 16, - lineHeight: "24px", - }, - lostKeyNotification: { - padding: "0 68px", - marginBottom: 32, - color: theme.palette.text.mediumShadeGray, - fontFamily: theme.typography.primary.main, - fontSize: 16, - fontWeight: "bold", - }, -}); From 63311be76fc96fe11d5f68e81e94fe438e11fc17 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:44:14 +0300 Subject: [PATCH 11/83] [SM-103] update switch account and network control --- src/Redux/reducers/PaymentReducer.js | 7 +- .../TokenPurchase/TokenPurchase.jsx | 13 +-- .../UserProfile/UserProfileAccount/index.js | 29 +++-- src/utility/sdk.js | 103 ++---------------- 4 files changed, 34 insertions(+), 118 deletions(-) diff --git a/src/Redux/reducers/PaymentReducer.js b/src/Redux/reducers/PaymentReducer.js index ab2f2b8b6..c22d1d251 100644 --- a/src/Redux/reducers/PaymentReducer.js +++ b/src/Redux/reducers/PaymentReducer.js @@ -1,11 +1,10 @@ -import { priceData } from "../../utility/PricingStrategy"; +import { DIVISIBILITY } from "../../utility/PricingStrategy"; import { paymentActions } from "../actionCreators"; const InitialPaymentDetails = { paypalInProgress: {}, usd_agi_rate: undefined, usd_cogs_rate: undefined, - divisibility: priceData.divisibility, }; const paymentReducer = (state = InitialPaymentDetails, action) => { @@ -50,11 +49,11 @@ export const anyFailedTxn = (state) => { return istransactionsFailed; }; -export const USDToAgi = (usd, usdFiatRate, fiatDivisibility) => { +export const USDToAgi = (usd, usdFiatRate) => { if (!usdFiatRate) { return undefined; } - return (usd * usdFiatRate).toFixed(fiatDivisibility); + return (usd * usdFiatRate).toFixed(DIVISIBILITY); }; export const USDToCogs = (usd, usdCogsRate) => { diff --git a/src/components/TokenPurchase/TokenPurchase.jsx b/src/components/TokenPurchase/TokenPurchase.jsx index a85d10a81..dac17f6f2 100644 --- a/src/components/TokenPurchase/TokenPurchase.jsx +++ b/src/components/TokenPurchase/TokenPurchase.jsx @@ -3,22 +3,21 @@ import { OnrampWebSDK } from '@onramp.money/onramp-web-sdk'; import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; import StyledButton from "../common/StyledButton"; -import { getWeb3Address } from "../../utility/sdk"; +import { useDispatch } from "react-redux"; +import { sdkActions } from "../../Redux/actionCreators"; const TokenPurchase = () => { + const dispatch = useDispatch(); const isMetamaskAvailable = window?.ethereum?.isMetaMask; - const showOrump = async () => { - console.log("isMetamaskAvailable: ", isMetamaskAvailable); - + const showOrump = async () => { if (!isMetamaskAvailable) { return; } try { - - const address = await getWeb3Address(); - console.log("address; ", address); + const sdk = await dispatch(sdkActions.getSdk()); + const address = await sdk.account.getAddress(); const onrampInstance = new OnrampWebSDK({ appId: 1, // replace this with the appID you got during onboarding process diff --git a/src/components/UserProfile/UserProfileAccount/index.js b/src/components/UserProfile/UserProfileAccount/index.js index 73695babe..a5bb29a07 100644 --- a/src/components/UserProfile/UserProfileAccount/index.js +++ b/src/components/UserProfile/UserProfileAccount/index.js @@ -17,8 +17,9 @@ import { startAppLoader, stopAppLoader } from "../../../Redux/actionCreators/Loa import { LoaderContent } from "../../../utility/constants/LoaderContent"; import { isEmpty } from "lodash"; import { CircularProgress } from "@mui/material"; -import { getWeb3Address, ON_ACCOUNT_CHANGE } from "../../../utility/sdk"; +import { ON_ACCOUNT_CHANGE } from "../../../utility/sdk"; import MetamaskAccount from "./MetamaskDetails"; +import { getSdk } from "../../../Redux/actionCreators/SDKActions"; const alertSelectCurrentWallet = { type: alertTypes.ERROR, @@ -39,16 +40,20 @@ const UserProfileAccount = ({ classes }) => { const getWallets = useCallback( async (currentAddress) => { - const availableWallets = await dispatch(fetchAvailableUserWallets()); - let currentWallet = availableWallets.find(({ address }) => address === currentAddress); - const enhancedWallets = availableWallets.map(({ address, type }) => parseWallet(address, type)); - if (!currentWallet) { - currentWallet = parseWallet(currentAddress, walletTypes.METAMASK); - enhancedWallets.push(currentWallet); + try { + const availableWallets = await dispatch(fetchAvailableUserWallets()); + let currentWallet = availableWallets.find(({ address }) => address === currentAddress); + const enhancedWallets = availableWallets.map(({ address, type }) => parseWallet(address, type)); + if (!currentWallet) { + currentWallet = parseWallet(currentAddress, walletTypes.METAMASK); + enhancedWallets.push(currentWallet); + } + setSelectedWallet(parseWallet(currentWallet.address, currentWallet.type)); + dispatch(userActions.updateWallet(currentWallet)); + return enhancedWallets; + } catch (err) { + console.error(err); } - setSelectedWallet(parseWallet(currentWallet.address, currentWallet.type)); - dispatch(userActions.updateWallet(currentWallet)); - return enhancedWallets; }, [dispatch] ); @@ -62,11 +67,13 @@ const UserProfileAccount = ({ classes }) => { const fetchWallets = useCallback(async () => { try { - const currentAddress = await getWeb3Address(); + const sdk = await dispatch(getSdk()); + const currentAddress = await sdk.account.getAddress(); const wallets = await getWallets(currentAddress); setCurrentAddress(currentAddress); setWallets(wallets); } catch (error) { + console.error("error: ", error); setCurrentAddress(""); setWallets([]); } diff --git a/src/utility/sdk.js b/src/utility/sdk.js index 1f113ab4f..b33832d4f 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -7,9 +7,6 @@ import { fetchAuthenticatedUser, walletTypes } from "../Redux/actionCreators/Use import PaypalPaymentMgmtStrategy from "./PaypalPaymentMgmtStrategy"; import { store } from "../"; import ProxyPaymentChannelManagementStrategy from "./ProxyPaymentChannelManagementStrategy"; -import { isEmpty, isUndefined } from "lodash"; -import Web3 from "web3"; -import { ethereumMethods } from "./snetSdk"; const DEFAULT_GAS_PRICE = 4700000; const DEFAULT_GAS_LIMIT = 210000; @@ -20,7 +17,6 @@ const EXPECTED_ID_ETHEREUM_NETWORK = Number(process.env.REACT_APP_ETH_NETWORK); let sdk; let channel; -let web3Provider; export const callTypes = { FREE: "FREE", @@ -47,10 +43,10 @@ const parseRegularCallMetadata = ({ data }) => ({ const parseFreeCallMetadata = ({ data }) => ({ "snet-payment-type": data["snet-payment-type"], "snet-free-call-user-id": data["snet-free-call-user-id"], - "snet-current-block-number": `${data["snet-current-block-number"]}`, + "snet-current-block-number": data["snet-current-block-number"], "snet-payment-channel-signature-bin": parseSignature(data["snet-payment-channel-signature-bin"]), "snet-free-call-auth-token-bin": parseSignature(data["snet-free-call-auth-token-bin"]), - "snet-free-call-token-expiry-block": `${data["snet-free-call-token-expiry-block"]}`, + "snet-free-call-token-expiry-block": data["snet-free-call-token-expiry-block"], "snet-payment-mpe-address": MPEContract[process.env.REACT_APP_ETH_NETWORK][process.env.REACT_APP_TOKEN_NAME][process.env.REACT_APP_STAND] .address, @@ -150,7 +146,7 @@ class PaypalSDK extends SnetSDK { } } -export const initPaypalSdk = (address, channelId) => { +export const initPaypalSdk = async (address, channelId) => { const config = { networkId: process.env.REACT_APP_ETH_NETWORK, web3Provider: process.env.REACT_APP_WEB3_PROVIDER, @@ -168,76 +164,13 @@ export const updateChannel = (newChannel) => { channel = newChannel; }; -const defineWeb3Provider = () => { - if (isUndefined(window.ethereum)) { - throw new Error("Metamask is not found"); - } - const web3 = new Web3(window.ethereum); - web3Provider = web3.eth; -}; - -const detectEthereumNetwork = async () => { - const chainIdHex = await web3Provider.getChainId(); - const networkId = parseInt(chainIdHex); - return networkId; -}; - -const isUserAtExpectedEthereumNetwork = async () => { - const currentNetworkId = await detectEthereumNetwork(); - return Number(currentNetworkId) === Number(EXPECTED_ID_ETHEREUM_NETWORK); -}; - -const switchNetwork = async () => { - const hexifiedChainId = "0x" + EXPECTED_ID_ETHEREUM_NETWORK.toString(16); - await window.ethereum.request({ - method: "wallet_switchEthereumChain", - params: [{ chainId: hexifiedChainId }], - }); -}; - -const clearSdk = () => { - sdk = undefined; -}; - -const addListenersForWeb3 = () => { - window.ethereum.addListener(ON_ACCOUNT_CHANGE, async (accounts) => { - await getWeb3Address(); - clearSdk(); - const event = new CustomEvent("snetMMAccountChanged", { bubbles: true, details: accounts[0] }); - window.dispatchEvent(event); - }); - window.ethereum.addListener(ON_NETWORK_CHANGE, (network) => { - switchNetwork(); - const event = new CustomEvent("snetMMNetworkChanged", { detail: { network } }); - window.dispatchEvent(event); - }); -}; - -export const getWeb3Address = async () => { - defineWeb3Provider(); - await window.ethereum.request({ method: ethereumMethods.REQUEST_ACCOUNTS }); - const isExpectedNetwork = await isUserAtExpectedEthereumNetwork(); - - if (!isExpectedNetwork) { - await switchNetwork(); - } - const accounts = await web3Provider.getAccounts(); - return isEmpty(accounts) ? undefined : accounts[0]; -}; - export const initSdk = async () => { if (sdk && !(sdk instanceof PaypalSDK)) { return Promise.resolve(sdk); } - defineWeb3Provider(); - addListenersForWeb3(); - const isExpectedNetwork = await isUserAtExpectedEthereumNetwork(); - if (!isExpectedNetwork) { - await switchNetwork(); - } const config = { - networkId: await detectEthereumNetwork(), + networkId: EXPECTED_ID_ETHEREUM_NETWORK, web3Provider: window.ethereum, defaultGasPrice: DEFAULT_GAS_PRICE, defaultGasLimit: DEFAULT_GAS_LIMIT, @@ -249,20 +182,6 @@ export const initSdk = async () => { return Promise.resolve(sdk); }; -export const getSdkConfig = async () => { - defineWeb3Provider(); - const config = { - networkId: await detectEthereumNetwork(), - web3Provider, - defaultGasPrice: DEFAULT_GAS_PRICE, - defaultGasLimit: DEFAULT_GAS_LIMIT, - tokenName: process.env.REACT_APP_TOKEN_NAME, - standType: process.env.REACT_APP_STAND, - }; - - return config; -}; - const getMethodNames = (service) => { const ownProperties = Object.getOwnPropertyNames(service); return ownProperties.filter((property) => { @@ -273,7 +192,7 @@ const getMethodNames = (service) => { }); }; -export const createServiceClient = ( +export const createServiceClient = async ( org_id, service_id, groupInfo, @@ -284,20 +203,12 @@ export const createServiceClient = ( wallet ) => { const options = generateOptions(callType, wallet, serviceRequestErrorHandler, groupInfo); + const metadataProvider = await sdk.createServiceMetadataProvider(org_id, service_id, groupInfo.group_name, options); let paymentChannelManagementStrategy = sdk && sdk._paymentChannelManagementStrategy; if (!(paymentChannelManagementStrategy instanceof PaypalPaymentMgmtStrategy)) { paymentChannelManagementStrategy = new ProxyPaymentChannelManagementStrategy(channel); } - const serviceClient = new ServiceClient( - sdk, - org_id, - service_id, - sdk && sdk._mpeContract, - {}, - process.env.REACT_APP_SANDBOX ? {} : groupInfo, - paymentChannelManagementStrategy, - options - ); + const serviceClient = new ServiceClient(metadataProvider, paymentChannelManagementStrategy, options); const finishServiceInteraction = () => { if (serviceRequestCompleteHandler) { From d5e11daa774ae97db6bc1222c43c5e0ce260f51f Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:44:52 +0300 Subject: [PATCH 12/83] [SM-103] update interraction with the service client --- .../AboutService/ServiceDemo/CompletedActions/index.js | 2 +- src/utility/PaypalPaymentMgmtStrategy.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js index 37f83fce8..9c7351581 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js @@ -32,7 +32,7 @@ const CompletedActions = ({ isComplete, callType, feedback, orgId, serviceId, re const getSignedAmountAndChannelId = useCallback(async () => { const sdk = await dispatch(getSdk()); - const serviceClient = await sdk.createServiceClient(orgId, serviceId); + const serviceClient = await sdk.createServiceClient({ orgId, serviceId }); const paymentChannelManagement = new PaymentChannelManagement(sdk, serviceClient); await paymentChannelManagement.updateChannelInfo(); const channel = paymentChannelManagement._channel; diff --git a/src/utility/PaypalPaymentMgmtStrategy.js b/src/utility/PaypalPaymentMgmtStrategy.js index 4f518945a..9c6ea8f52 100644 --- a/src/utility/PaypalPaymentMgmtStrategy.js +++ b/src/utility/PaypalPaymentMgmtStrategy.js @@ -18,7 +18,7 @@ export default class PaypalPaymentChannelMgmtStrategy { this._channelId, this._sdk.web3, this._sdk.account, - serviceClient, + serviceClient.metadataProvider, this._sdk._mpeContract ); } From 16769dc39b5476e79a5f4a0c4dd1b2711b8e334b Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:45:49 +0300 Subject: [PATCH 13/83] [SM-103] update getting of divisibility --- src/Redux/actionCreators/SDKActions.js | 5 ++--- .../PaymentPopup/Details/index.js | 6 +++--- .../Details/validationConstraints.js | 11 +++------- src/utility/PricingStrategy.js | 21 ++++++++----------- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/Redux/actionCreators/SDKActions.js b/src/Redux/actionCreators/SDKActions.js index c5bee147d..dd3ebb3d8 100644 --- a/src/Redux/actionCreators/SDKActions.js +++ b/src/Redux/actionCreators/SDKActions.js @@ -1,5 +1,5 @@ import { isEmpty } from "lodash"; -import { getWeb3Address, initSdk } from "../../utility/sdk"; +import { initSdk } from "../../utility/sdk"; export const SET_SDK = "SET_SDK"; export const SET_SERVICE_CLIENT = "SET_SERVICE_CLIENT"; @@ -24,13 +24,12 @@ export const initializingSdk = () => async (dispatch) => { const initializeServiceClient = (organizationId, serviceId) => async (dispatch) => { const sdk = await dispatch(getSdk()); - const serviceClient = await sdk.createServiceClient(organizationId, serviceId); + const serviceClient = await sdk.createServiceClient({ orgId: organizationId, serviceId }); // dispatch(updateServiceClient(serviceClient)) return serviceClient; }; export const getSdk = () => async (dispatch, getState) => { - await getWeb3Address(); let sdk = getState().sdkReducer.sdk; if (!isEmpty(sdk)) { return sdk; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js index 6b6d5b567..e4c30da11 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js @@ -34,7 +34,7 @@ const Details = ({ classes, handleClose, orderType, handleNextSection }) => { const walletList = useSelector((state) => state.userReducer.walletList); const generalWallet = walletList.find((wallet) => wallet.type === walletTypes.GENERAL); - const { usd_agi_rate, divisibility } = useSelector((state) => state.paymentReducer); + const { usd_agi_rate } = useSelector((state) => state.paymentReducer); const groupInfo = useSelector((state) => { return state.serviceDetailsReducer.details.groups.find((group) => { return !isEmpty(group.endpoints.find((endpoint) => endpoint.is_available === 1)); @@ -101,7 +101,7 @@ const Details = ({ classes, handleClose, orderType, handleNextSection }) => { setAlert({}); try { if (initiateInProcess) return; - const amountInAGI = USDToAgi(amount, usd_agi_rate, divisibility); + const amountInAGI = USDToAgi(amount, usd_agi_rate); initiateInProcess = true; await initiatePayment(amount, currency, process.env.REACT_APP_TOKEN_NAME, amountInAGI, generalWallet?.address); @@ -125,7 +125,7 @@ const Details = ({ classes, handleClose, orderType, handleNextSection }) => { value={amount} placeholder="0" onChange={(e) => handleAmountChange(e)} - helperText={amountError ? amountError : } + helperText={amountError ? amountError : } InputProps={{ startAdornment: $ }} error={Boolean(amountError)} /> diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/validationConstraints.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/validationConstraints.js index 4662b8ae2..dff3f36c8 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/validationConstraints.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/validationConstraints.js @@ -1,12 +1,6 @@ -// const allowedPayTypes = ["paypal"]; - -import { priceData } from "../../../../../../../../../utility/PricingStrategy"; +import { DIVISIBILITY } from "../../../../../../../../../utility/PricingStrategy"; export const paymentGatewayConstraints = { - // payType: { - // presence: { allowEmpty: false }, - // inclusion: { within: allowedPayTypes, message: "is not valid" }, - // }, amount: { presence: { isNumber: true, allowEmpty: true }, numericality: { lessThan: 10000 }, @@ -17,9 +11,10 @@ export const FLOAT_MAXIMUM_DIGIT_CASES = { DEFAULT: "DEFAULT", TOKEN: "TOKEN", }; + const FLOAT_MAXIMUM_DIGIT = { [FLOAT_MAXIMUM_DIGIT_CASES.DEFAULT]: 2, - [FLOAT_MAXIMUM_DIGIT_CASES.TOKEN]: priceData.divisibility, + [FLOAT_MAXIMUM_DIGIT_CASES.TOKEN]: DIVISIBILITY, }; const AVAILABLE_SEPARATORS = [",", "."]; diff --git a/src/utility/PricingStrategy.js b/src/utility/PricingStrategy.js index 6a482364a..c50334b60 100644 --- a/src/utility/PricingStrategy.js +++ b/src/utility/PricingStrategy.js @@ -1,18 +1,15 @@ -const priceDataByToken = { - FET: { - precision: 1000000000000000000, - divisibility: 18, - }, - AGIX: { - precision: 100000000, - divisibility: 8, - }, +import { cogsToToken as cogsToTokenSDK } from "snet-sdk-web/utils/tokenUtils"; + +const TOKEN_DIVISIBILITY = { + FET: 18, + AGIX: 8, }; -export const priceData = { +export const DIVISIBILITY = TOKEN_DIVISIBILITY[process.env.REACT_APP_TOKEN_NAME]; + +const priceData = { fixed_price_model: "fixed_price", fixed_price_per_method: "fixed_price_per_method", - ...priceDataByToken[process.env.REACT_APP_TOKEN_NAME], }; const priceModelNames = { @@ -106,7 +103,7 @@ class MethodPricing { } } -export const cogsToAgi = (cogs) => (Number(cogs) / priceData.precision).toFixed(priceData.divisibility); +export const cogsToToken = (cogs) => cogsToTokenSDK(cogs, [process.env.REACT_APP_TOKEN_NAME]); export const agiToCogs = (agi) => Math.round(agi * priceData.precision); From b3159765f7183422196e598b09ff02a3f5d6463c Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:47:50 +0300 Subject: [PATCH 14/83] [SM-103] update cogs to token conversion --- src/Redux/reducers/UserReducer.js | 6 +++--- .../ExpiredSession/MetamaskFlow/index.js | 16 ++++++++-------- .../MetamaskDetails/MetamaskDetails.js | 7 +++---- .../ProviderBalance/ChannelList/index.js | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Redux/reducers/UserReducer.js b/src/Redux/reducers/UserReducer.js index 06ed5ea49..50cf42399 100644 --- a/src/Redux/reducers/UserReducer.js +++ b/src/Redux/reducers/UserReducer.js @@ -2,7 +2,7 @@ import isEmpty from "lodash/isEmpty"; import { userActions } from "../actionCreators"; import { walletTypes, RESET_LOGIN_ERROR } from "../actionCreators/UserActions"; -import { cogsToAgi } from "../../utility/PricingStrategy"; +import { cogsToToken } from "../../utility/PricingStrategy"; export const initialWallet = { value: walletTypes.DEFAULT, type: walletTypes.METAMASK }; const InitialUserDetails = { @@ -140,8 +140,8 @@ export const channelInfo = (walletList) => { return { id: selectedChannel.channel_id, hasPrivateKey: Boolean(walletWithChannel.has_private_key), - balanceInAgi: cogsToAgi(selectedChannel.balance_in_cogs), - currentBalance: cogsToAgi(selectedChannel.current_balance), + balanceInAgi: cogsToToken(selectedChannel.balance_in_cogs), + currentBalance: cogsToToken(selectedChannel.current_balance), walletaddress: walletWithChannel.address, }; } diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index 95f91b990..04ec1dc2f 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -7,7 +7,7 @@ import PaymentInfoCard from "../../PaymentInfoCard"; import PurchaseDialog from "../../PurchaseDialog"; import ChannelSelectionBox from "../../ChannelSelectionBox"; import AlertBox, { alertTypes } from "../../../../../../common/AlertBox"; -import { cogsToAgi } from "../../../../../../../utility/PricingStrategy"; +import { cogsToToken } from "../../../../../../../utility/PricingStrategy"; import { pricing as getPricing } from "../../../../../../../Redux/reducers/ServiceDetailsReducer"; import PaymentChannelManagement from "../../../../../../../utility/PaymentChannelManagement"; import { loaderActions } from "../../../../../../../Redux/actionCreators"; @@ -51,7 +51,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva const [disabledPayTypes, setDisabledPayTypes] = useState([]); const [showPurchaseDialog, setShowPurchaseDialog] = useState(false); const [noOfServiceCalls, setNoOfServiceCalls] = useState(1); - const [totalPrice, setTotalPrice] = useState(cogsToAgi(price_in_cogs)); + const [totalPrice, setTotalPrice] = useState(cogsToToken(price_in_cogs)); const [alert, setAlert] = useState({}); const [showTooltip, setShowTooltip] = useState(false); const [channelBalance, setChannelBalance] = useState(); @@ -95,10 +95,10 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva setAlert({}); dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); const sdk = await dispatch(getSdk()); - const serviceClient = await sdk.createServiceClient(org_id, service_id); + const serviceClient = await sdk.createServiceClient({ orgId: org_id, serviceId: service_id }); paymentChannelManagement = new PaymentChannelManagement(sdk, serviceClient); const escrowBalance = await sdk.account.escrowBalance(); - setMpeBalance(cogsToAgi(escrowBalance)); + setMpeBalance(cogsToToken(escrowBalance)); } catch (error) { console.error("error on initialize Metamask payment channel: ", error); setAlert(connectMMinfo); @@ -126,7 +126,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva try { await dispatch(updateMetamaskWallet()); const channelBalance = paymentChannelManagement.availableBalance(); - const channelBalanceInCogs = cogsToAgi(channelBalance); + const channelBalanceInCogs = cogsToToken(channelBalance); setChannelBalance(channelBalanceInCogs); if (channelBalanceInCogs === totalPrice) { setIsLastPaidCall(true); @@ -191,7 +191,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva if (!isValidCallsNumber(noOfServiceCalls)) { return; } - const totalPriceInCogs = cogsToAgi(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls)); + const totalPriceInCogs = cogsToToken(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls)); setTotalPrice(totalPriceInCogs); }; @@ -221,7 +221,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva } try { - if (mpeBalance < cogsToAgi(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls))) { + if (mpeBalance < cogsToToken(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls))) { setAlert({ type: alertTypes.ERROR, message: `Insufficient MPE balance. Please deposit some ${process.env.REACT_APP_TOKEN_NAME} tokens to your escrow account`, @@ -293,7 +293,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva value={payTypes.SINGLE_CALL} onClick={() => handlePayTypeChange(payTypes.SINGLE_CALL)} inputProps={{ - totalPrice: cogsToAgi(price_in_cogs), + totalPrice: cogsToToken(price_in_cogs), unit: process.env.REACT_APP_TOKEN_NAME, noInput: true, }} diff --git a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js index cef91788e..40903d586 100644 --- a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js +++ b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; -import { cogsToAgi } from "../../../../utility/PricingStrategy"; +import { cogsToToken } from "../../../../utility/PricingStrategy"; import { loaderActions, sdkActions } from "../../../../Redux/actionCreators"; import { LoaderContent } from "../../../../utility/constants/LoaderContent"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; @@ -11,7 +11,6 @@ import { Networks } from "../../../../config/Networks"; const MetamaskDetails = ({ classes }) => { const wallet = useSelector((state) => state.userReducer.wallet); - const [tokenBalance, setTokenBalance] = useState(""); const [escrowBalance, setEscrowBalance] = useState(""); const [alert, setAlert] = useState({}); @@ -26,8 +25,8 @@ const MetamaskDetails = ({ classes }) => { const escrowBalance = await sdk.account.escrowBalance(); const tokenBalance = await sdk.account.balance(); - setEscrowBalance(cogsToAgi(escrowBalance)); - setTokenBalance(cogsToAgi(tokenBalance)); + setEscrowBalance(cogsToToken(escrowBalance)); + setTokenBalance(cogsToToken(tokenBalance)); } catch (error) { console.error("error: ", error); setAlert({ type: alertTypes.ERROR, message: `Unable to fetch account details` }); diff --git a/src/components/UserProfile/UserProfileAccount/ProviderBalance/ChannelList/index.js b/src/components/UserProfile/UserProfileAccount/ProviderBalance/ChannelList/index.js index 566b4b638..ddfd2ff43 100644 --- a/src/components/UserProfile/UserProfileAccount/ProviderBalance/ChannelList/index.js +++ b/src/components/UserProfile/UserProfileAccount/ProviderBalance/ChannelList/index.js @@ -6,7 +6,7 @@ import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; import SingularityLogo from "../../../../../assets/images/avatar.png"; -import { cogsToAgi } from "../../../../../utility/PricingStrategy"; +import { cogsToToken } from "../../../../../utility/PricingStrategy"; const ChannelList = ({ classes, linkedProviders }) => { return ( @@ -35,7 +35,7 @@ const ChannelList = ({ classes, linkedProviders }) => { - {cogsToAgi(current_balance || 0)} {process.env.REACT_APP_TOKEN_NAME} + {cogsToToken(current_balance || 0)} {process.env.REACT_APP_TOKEN_NAME} From 4c9b88dc8bb79d0de2f5a5100290f9ce58dd16f6 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 29 May 2025 10:48:21 +0300 Subject: [PATCH 15/83] [SM-103] update config overrides --- config-overrides.js | 1 + 1 file changed, 1 insertion(+) diff --git a/config-overrides.js b/config-overrides.js index cca9fe852..7b6e2f1ec 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -27,6 +27,7 @@ module.exports = function override(config) { os: require.resolve("os-browserify"), url: require.resolve("url"), path: require.resolve("path-browserify"), + fs: require.resolve("fs"), }); config.resolve.fallback = fallback; config.plugins = (config.plugins || []).concat([ From d5d31c1f687778747026f5bf5fc19152955dd244 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Fri, 30 May 2025 13:29:44 +0300 Subject: [PATCH 16/83] [SM-105] update free-calls system --- .../actionCreators/ServiceDetailsActions.js | 15 ++++---- src/Redux/reducers/ServiceDetailsReducer.js | 33 +++++++++-------- src/components/Hooks/useLocalStorage.js | 1 + .../Purchase/ActiveSession/index.js | 8 ++--- .../ServiceDemo/Purchase/index.js | 27 ++++++++------ .../ServiceDemo/ThirdPartyAIService.js | 8 ++--- src/config/APIEndpoints.js | 4 +-- src/utility/sdk.js | 36 ++++++++++++------- 8 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index fd8cbb3d4..2e95241c4 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -51,7 +51,10 @@ export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { const fetchMeteringDataSuccess = (usageData) => (dispatch) => { dispatch({ type: UPDATE_FREE_CALLS_INFO, - payload: usageData.total_calls_made, + payload: { + freeCallsTotal: usageData.free_calls_total, + freeCallsAvailable: usageData.free_calls_available, + }, }); }; @@ -75,19 +78,19 @@ export const fetchTrainingModel = (orgId, serviceId) => async (dispatch) => { dispatch(fetchTrainingModelSuccess(serviceTrainingData)); }; -const meteringAPI = (token, orgId, serviceId, groupId, userId) => { - const apiName = APIEndpoints.USER.name; +const meteringAPI = (token, orgId, serviceId, groupId, freeCallToken) => { + const apiName = APIEndpoints.SIGNER_SERVICE.name; const apiPath = APIPaths.FREE_CALL_USAGE; - const queryParams = { organization_id: orgId, service_id: serviceId, group_id: groupId, username: userId }; + const queryParams = { org_id: orgId, service_id: serviceId, group_id: groupId, freecall_token: freeCallToken }; const apiOptions = initializeAPIOptions(token, null, queryParams); return getAPI(apiName, apiPath, apiOptions); }; export const fetchMeteringData = - ({ orgId, serviceId, groupId }) => + ({ orgId, serviceId, groupId, freeCallToken }) => async (dispatch) => { const { email, token } = await dispatch(fetchAuthenticatedUser()); - const usageData = await meteringAPI(token, orgId, serviceId, groupId, email); + const usageData = await meteringAPI(token, orgId, serviceId, groupId, email, freeCallToken); dispatch(fetchMeteringDataSuccess(usageData)); return usageData; }; diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index 6438555db..42ea4533d 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -6,7 +6,10 @@ import map from "lodash/map"; const InitialServiceDetails = { details: {}, - freeCallsUsed: "", + freeCalls: { + freeCallsTotal: "", + freeCallsAvailable: "", + }, detailsTraining: {}, }; @@ -19,7 +22,7 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { return { ...state, details: action.payload }; } case serviceDetailsActions.UPDATE_FREE_CALLS_INFO: { - return { ...state, freeCallsUsed: action.payload }; + return { ...state, freeCalls: action.payload }; } case serviceDetailsActions.UPDATE_TRAINING_DETAILS: { return { ...state, detailsTraining: action.payload }; @@ -30,19 +33,19 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { } }; -export const freeCalls = (state) => { - const selectedGroup = groupInfo(state); - if (!selectedGroup) { - return {}; - } - if (selectedGroup.free_calls === 0) { - return { allowed: 0, remaining: 0 }; - } - return { - allowed: selectedGroup.free_calls, - remaining: selectedGroup.free_calls - state.serviceDetailsReducer.freeCallsUsed, - }; -}; +// export const freeCalls = (state) => { +// const selectedGroup = groupInfo(state); +// if (!selectedGroup) { +// return {}; +// } +// if (selectedGroup.free_calls === 0) { +// return { allowed: 0, remaining: 0 }; +// } +// return { +// allowed: selectedGroup.free_calls, +// remaining: selectedGroup.free_calls - state.serviceDetailsReducer.freeCallsUsed, +// }; +// }; export const currentServiceDetails = (state) => { return state.serviceDetailsReducer.details; diff --git a/src/components/Hooks/useLocalStorage.js b/src/components/Hooks/useLocalStorage.js index c47100bc2..2f11f7c55 100644 --- a/src/components/Hooks/useLocalStorage.js +++ b/src/components/Hooks/useLocalStorage.js @@ -36,6 +36,7 @@ function useLocalStorage(key, initialValue) { const localStorageKeys = { SHOW_PHASE2_NOTIFICATION: "SHOW_PHASE2_NOTIFICATION", + FREE_CALL_TOKEN: "FREE_CALL_TOKEN", }; export { useLocalStorage, localStorageKeys }; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js index 9a92a306e..d2830b466 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js @@ -13,7 +13,7 @@ import { currentServiceDetails } from "../../../../../../Redux/reducers/ServiceD import { isUndefined } from "lodash"; import { updateMetamaskWallet } from "../../../../../../Redux/actionCreators/UserActions"; -const ActiveSession = ({ classes, freeCallsRemaining, handleComplete, freeCallsAllowed, isServiceAvailable }) => { +const ActiveSession = ({ classes, freeCallsAvailable, handleComplete, freeCallsTotal, isServiceAvailable }) => { const dispatch = useDispatch(); const { detailsTraining } = useSelector((state) => state.serviceDetailsReducer); const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); @@ -21,7 +21,7 @@ const ActiveSession = ({ classes, freeCallsRemaining, handleComplete, freeCallsA const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const [showTooltip, setShowTooltip] = useState(false); - const progressValue = () => (freeCallsRemaining / freeCallsAllowed) * 100; + const progressValue = () => (freeCallsAvailable / freeCallsTotal) * 100; const handleTooltipOpen = () => { if (!isServiceAvailable) { @@ -46,12 +46,12 @@ const ActiveSession = ({ classes, freeCallsRemaining, handleComplete, freeCallsA
Free API Calls - {freeCallsRemaining} + {freeCallsAvailable}
{ const dispatch = useDispatch(); @@ -16,31 +17,37 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set const group_id = useSelector((state) => groupInfo(state).group_id); const email = useSelector((state) => state.userReducer.email); + const [freeCallToken, setFreeCallToken] = useLocalStorage(localStorageKeys.FREE_CALL_TOKEN, ""); + const [isFreecallLoading, setIsFreecallLoading] = useState(false); - const [freeCalls, setFreeCalls] = useState({ freeCallsAllowed: undefined, freeCallsRemaining: undefined }); + const [freeCalls, setFreeCalls] = useState({ freeCallsTotal: undefined, freeCallsAvailable: undefined }); useEffect(() => { const fetchFreeCallsUsage = async () => { dispatch(loaderActions.startAppLoader(LoaderContent.FREE_CALLS_GETTING)); try { setIsFreecallLoading(true); - const { free_calls_allowed, total_calls_made } = await dispatch( + const { free_calls_total, free_calls_available, free_call_token_hex } = await dispatch( serviceDetailsActions.fetchMeteringData({ orgId: org_id, serviceId: service_id, groupId: group_id, username: email, + freeCallToken, }) ); + console.log("freeCallToken: ", freeCallToken, "new: ", free_call_token_hex); + + setFreeCallToken(free_call_token_hex); setFreeCalls({ - freeCallsAllowed: free_calls_allowed, - freeCallsRemaining: free_calls_allowed - total_calls_made, + freeCallsTotal: free_calls_total, + freeCallsAvailable: free_calls_available, }); } catch (error) { console.error(error); setFreeCalls({ - freeCallsAllowed: 0, - freeCallsRemaining: 0, + freeCallsTotal: 0, + freeCallsAvailable: 0, }); } finally { setIsFreecallLoading(false); @@ -51,7 +58,7 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set fetchFreeCallsUsage(); }, [dispatch, org_id, service_id, group_id, email]); - if (isFreecallLoading || isUndefined(freeCalls.freeCallsRemaining)) { + if (isFreecallLoading || isUndefined(freeCalls.freeCallsAvailable)) { return (
@@ -59,7 +66,7 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set ); } - if (freeCalls.freeCallsRemaining < 1) { + if (freeCalls.freeCallsAvailable < 1) { return ( diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js index 849da0b45..b7bb9c3e7 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js @@ -11,7 +11,7 @@ import ThirdPartyServiceErrorBoundary from "./ThirdPartyServiceErrorBoundary"; import { channelInfo } from "../../../../Redux/reducers/UserReducer"; import { isEmpty } from "lodash"; import { modelStatus } from "../../../../Redux/reducers/ServiceTrainingReducer"; -import { freeCalls, groupInfo } from "../../../../Redux/reducers/ServiceDetailsReducer"; +import { groupInfo } from "../../../../Redux/reducers/ServiceDetailsReducer"; class ThirdPartyAIService extends Component { state = { @@ -23,8 +23,8 @@ class ThirdPartyAIService extends Component { }; componentDidMount = async () => { - const { org_id, service_id, freeCallsRemaining, groupInfo, wallet } = this.props; - const callType = freeCallsRemaining > 0 ? callTypes.FREE : callTypes.REGULAR; + const { org_id, service_id, freeCallsAvailable, groupInfo, wallet } = this.props; + const callType = freeCallsAvailable > 0 ? callTypes.FREE : callTypes.REGULAR; this.serviceClient = await createServiceClient( org_id, service_id, @@ -120,7 +120,7 @@ const mapStateToProps = (state) => ({ wallet: state.userReducer.wallet, channelInfo: channelInfo(state.userReducer.walletList), groupInfo: groupInfo(state), - freeCallsRemaining: freeCalls(state).remaining, + freeCallsAvailable: state.serviceDetailsReducer.freeCalls.freeCallsAvailable, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/src/config/APIEndpoints.js b/src/config/APIEndpoints.js index 0ffe5c2cc..a8b40727c 100644 --- a/src/config/APIEndpoints.js +++ b/src/config/APIEndpoints.js @@ -36,7 +36,7 @@ export const APIPaths = { GET_USER_PROFILE: "/profile", UPDATE_USER_PROFILE: "/profile", DELETE_USER: "/user/delete", - FREE_CALL_USAGE: "/usage/freecalls", + FREE_CALL_USAGE: "/freecall/usage", FEEDBACK: "/feedback", GET_SERVICE_LIST: "/service", FILTER_DATA: "/service?attribute=", @@ -45,7 +45,7 @@ export const APIPaths = { UPDATE_CHANNEL_BALANCE: (channelId) => `/channel/${channelId}/balance`, LINKED_PROVIDERS: "/v2/channel", FREE_CALL_TOKEN: "/free-call/token", - SIGNER_FREE_CALL: "/free-call", + SIGNER_FREE_CALL: "/freecall", SIGNER_STATE_SERVICE: "/state-service", GET_SIGNATURE: "/sign-call", SIGNER_REGULAR_CALL: "/regular-call", diff --git a/src/utility/sdk.js b/src/utility/sdk.js index 1f113ab4f..f143188da 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -10,6 +10,7 @@ import ProxyPaymentChannelManagementStrategy from "./ProxyPaymentChannelManageme import { isEmpty, isUndefined } from "lodash"; import Web3 from "web3"; import { ethereumMethods } from "./snetSdk"; +import { localStorageKeys } from "../components/Hooks/useLocalStorage"; const DEFAULT_GAS_PRICE = 4700000; const DEFAULT_GAS_LIMIT = 210000; @@ -44,23 +45,34 @@ const parseRegularCallMetadata = ({ data }) => ({ .address, }); -const parseFreeCallMetadata = ({ data }) => ({ - "snet-payment-type": data["snet-payment-type"], - "snet-free-call-user-id": data["snet-free-call-user-id"], - "snet-current-block-number": `${data["snet-current-block-number"]}`, - "snet-payment-channel-signature-bin": parseSignature(data["snet-payment-channel-signature-bin"]), - "snet-free-call-auth-token-bin": parseSignature(data["snet-free-call-auth-token-bin"]), - "snet-free-call-token-expiry-block": `${data["snet-free-call-token-expiry-block"]}`, - "snet-payment-mpe-address": - MPEContract[process.env.REACT_APP_ETH_NETWORK][process.env.REACT_APP_TOKEN_NAME][process.env.REACT_APP_STAND] - .address, -}); +const parseFreeCallMetadata = ({ data }) => { + window.localStorage.setItem(localStorageKeys.FREE_CALL_TOKEN, JSON.stringify(data.freecall_token)); + return { + "snet-payment-type": data.payment_type, + "snet-free-call-user-id": data.user_id, + "snet-current-block-number": data.block_number, + "snet-payment-channel-signature-bin": parseSignature(data.signature), + "snet-free-call-auth-token-bin": parseSignature(data.freecall_token), + "snet-payment-mpe-address": + MPEContract[process.env.REACT_APP_ETH_NETWORK][process.env.REACT_APP_TOKEN_NAME][process.env.REACT_APP_STAND] + .address, + }; +}; const metadataGenerator = (serviceRequestErrorHandler, groupId) => async (serviceClient, serviceName, method) => { try { const { orgId: org_id, serviceId: service_id } = serviceClient.metadata; const { email, token } = await store.dispatch(fetchAuthenticatedUser()); - const payload = { org_id, service_id, service_name: serviceName, method, username: email, group_id: groupId }; + const freeCallToken = window.localStorage.getItem(localStorageKeys.FREE_CALL_TOKEN); + const payload = { + org_id, + service_id, + service_name: serviceName, + method, + username: email, + group_id: groupId, + freecall_token: freeCallToken, + }; const apiName = APIEndpoints.SIGNER_SERVICE.name; const apiOptions = initializeAPIOptions(token, payload); const meta = await postAPI(apiName, APIPaths.SIGNER_FREE_CALL, apiOptions); From 979b3abd3ce1c1739caff0fcb113156e893c73b2 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Fri, 30 May 2025 13:32:06 +0300 Subject: [PATCH 17/83] update gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 62f81ec1d..2d335089a 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ typings/ # dotenv environment variables file .env -.env*.local +.env* # next.js build output .next From bd2ba27c5c2ad85b2791efc48c8ca73123de5751 Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Tue, 3 Jun 2025 02:31:38 +0300 Subject: [PATCH 18/83] Create yarn_audit.yml --- .github/workflows/yarn_audit.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/yarn_audit.yml diff --git a/.github/workflows/yarn_audit.yml b/.github/workflows/yarn_audit.yml new file mode 100644 index 000000000..a1ce781bf --- /dev/null +++ b/.github/workflows/yarn_audit.yml @@ -0,0 +1,26 @@ +name: Yarn Security Audit + +on: + push: + branches: [development, master] + pull_request: + branches: [development, master] + +jobs: + yarn-audit: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run yarn audit (log only) + run: yarn audit From c74610ce3d8fb25d7ca6e0034a94fd8d1a6fad8c Mon Sep 17 00:00:00 2001 From: Dias Bolatov <55557388+BANGLADESH228@users.noreply.github.com> Date: Tue, 3 Jun 2025 02:32:02 +0300 Subject: [PATCH 19/83] Create yarn_audit.yml --- .github/workflows/yarn_audit.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/yarn_audit.yml diff --git a/.github/workflows/yarn_audit.yml b/.github/workflows/yarn_audit.yml new file mode 100644 index 000000000..a1ce781bf --- /dev/null +++ b/.github/workflows/yarn_audit.yml @@ -0,0 +1,26 @@ +name: Yarn Security Audit + +on: + push: + branches: [development, master] + pull_request: + branches: [development, master] + +jobs: + yarn-audit: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run yarn audit (log only) + run: yarn audit From 33bb8491a13d0c5ea81aa9f2f835dc2b44e21d6e Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 4 Jun 2025 13:30:24 +0300 Subject: [PATCH 20/83] fix dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 85649f2b1..72ec5516e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "@mui/material": "^5.16.6", "@mui/styles": "^5.16.6", "@onramp.money/onramp-web-sdk": "1.5.7", + "@sentry-internal/feedback": "^9.25.1", "@sentry/browser": "^8.23.0", "@sentry/react": "^9.5.0", "aws-amplify": "^6.4.4", From d77180ae523280b05bb0650ad60c9db6d94ec915 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:20:01 +0300 Subject: [PATCH 21/83] [SM-103] update config overrides --- config-overrides.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config-overrides.js b/config-overrides.js index 7b6e2f1ec..39580e3d8 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -22,12 +22,12 @@ const options = { module.exports = function override(config) { const modifiedConfig = aliasWebpack(options)(config); - const fallback = config.resolve.fallback || {}; + let fallback = config.resolve.fallback || {}; + fallback = { ...fallback, fs: false }; Object.assign(fallback, { os: require.resolve("os-browserify"), url: require.resolve("url"), path: require.resolve("path-browserify"), - fs: require.resolve("fs"), }); config.resolve.fallback = fallback; config.plugins = (config.plugins || []).concat([ diff --git a/package.json b/package.json index 72ec5516e..4c093adb2 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "redux-thunk": "^3.1.0", "singularitynet-platform-contracts": "2.1.0", "slick-carousel": "^1.8.1", - "snet-sdk-web": "4.2.2", + "snet-sdk-web": "5.0.3", "utf8": "^3.0.0", "validate.js": "^0.13.1", "web3": "^4.11.1" From 6c93e7782f5a172dbed667fe8da097f8daf653b2 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:21:51 +0300 Subject: [PATCH 22/83] [SM-103] update cogs to token conversion --- src/utility/PricingStrategy.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/utility/PricingStrategy.js b/src/utility/PricingStrategy.js index c50334b60..e6cdcf6b5 100644 --- a/src/utility/PricingStrategy.js +++ b/src/utility/PricingStrategy.js @@ -44,13 +44,7 @@ export class PricingStrategy { } getMaxPriceInAGI() { - return AGIUtils.inAGI(this.pricingModel.getMaxPriceInCogs()); - } -} - -class AGIUtils { - static inAGI(cogs) { - return (cogs / priceData.precision).toFixed(priceData.divisibility); + return cogsToToken(this.pricingModel.getMaxPriceInCogs()); } } @@ -64,7 +58,7 @@ class FixedPricing { } getPriceInAGI(serviceName, methodName) { - return AGIUtils.inAGI(this.price_in_cogs); + return cogsToToken(this.priceInCogs); } getMaxPriceInCogs() { @@ -95,7 +89,7 @@ class MethodPricing { getPriceInAGI(serviceName, methodName) { const priceInCogs = this.getPriceInCogs(serviceName, methodName); - return AGIUtils.inAGI(priceInCogs); + return priceInCogs(priceInCogs); } getMaxPriceInCogs() { From 772aaf854d5477b0805a8df7ad4640b7a908cabc Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:23:07 +0300 Subject: [PATCH 23/83] [SM-105] remove free-call token from local storage --- src/components/Hooks/useLocalStorage.js | 1 - .../ServiceDemo/Purchase/index.js | 23 +++--- src/utility/sdk.js | 72 ++++++++++--------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/components/Hooks/useLocalStorage.js b/src/components/Hooks/useLocalStorage.js index 2f11f7c55..c47100bc2 100644 --- a/src/components/Hooks/useLocalStorage.js +++ b/src/components/Hooks/useLocalStorage.js @@ -36,7 +36,6 @@ function useLocalStorage(key, initialValue) { const localStorageKeys = { SHOW_PHASE2_NOTIFICATION: "SHOW_PHASE2_NOTIFICATION", - FREE_CALL_TOKEN: "FREE_CALL_TOKEN", }; export { useLocalStorage, localStorageKeys }; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js index 7939f9afd..a6ba2a83c 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js @@ -2,23 +2,20 @@ import React, { useEffect, useState } from "react"; import ActiveSession from "./ActiveSession"; import ExpiredSession from "./ExpiredSession"; -import { currentServiceDetails, groupInfo } from "../../../../../Redux/reducers/ServiceDetailsReducer"; +import { groupInfo } from "../../../../../Redux/reducers/ServiceDetailsReducer"; import { useDispatch, useSelector } from "react-redux"; import { loaderActions, serviceDetailsActions } from "../../../../../Redux/actionCreators"; import { LoaderContent } from "../../../../../utility/constants/LoaderContent"; import CircularProgress from "@material-ui/core/CircularProgress"; import "./styles.css"; import { isUndefined } from "lodash"; -import { localStorageKeys, useLocalStorage } from "../../../../Hooks/useLocalStorage"; const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, setIsLastPaidCall }) => { const dispatch = useDispatch(); - const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); - const group_id = useSelector((state) => groupInfo(state).group_id); + const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); + const { free_calls, group_id } = useSelector((state) => groupInfo(state)); const email = useSelector((state) => state.userReducer.email); - const [freeCallToken, setFreeCallToken] = useLocalStorage(localStorageKeys.FREE_CALL_TOKEN, ""); - const [isFreecallLoading, setIsFreecallLoading] = useState(false); const [freeCalls, setFreeCalls] = useState({ freeCallsTotal: undefined, freeCallsAvailable: undefined }); @@ -27,21 +24,19 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set dispatch(loaderActions.startAppLoader(LoaderContent.FREE_CALLS_GETTING)); try { setIsFreecallLoading(true); - const { free_calls_total, free_calls_available, free_call_token_hex } = await dispatch( + const { freeCallsAvailable, freeCallsTotal } = await dispatch( serviceDetailsActions.fetchMeteringData({ orgId: org_id, serviceId: service_id, groupId: group_id, - username: email, - freeCallToken, + freeCallsTotal: free_calls, }) ); - console.log("freeCallToken: ", freeCallToken, "new: ", free_call_token_hex); + console.log(freeCallsTotal, freeCallsAvailable); - setFreeCallToken(free_call_token_hex); setFreeCalls({ - freeCallsTotal: free_calls_total, - freeCallsAvailable: free_calls_available, + freeCallsTotal, + freeCallsAvailable, }); } catch (error) { console.error(error); @@ -56,7 +51,7 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set }; fetchFreeCallsUsage(); - }, [dispatch, org_id, service_id, group_id, email]); + }, [dispatch, org_id, service_id, group_id, email, free_calls]); if (isFreecallLoading || isUndefined(freeCalls.freeCallsAvailable)) { return ( diff --git a/src/utility/sdk.js b/src/utility/sdk.js index e0bcf93d8..e03330e08 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -1,4 +1,5 @@ -import SnetSDK, { WebServiceClient as ServiceClient } from "snet-sdk-web"; +import SnetSDK from "snet-sdk-web"; +import { FreecallMetadataGenerator, hexStringToBytes } from "snet-sdk-web/utils"; import MPEContract from "singularitynet-platform-contracts/networks/MultiPartyEscrow"; import { APIEndpoints, APIPaths } from "../config/APIEndpoints"; @@ -7,7 +8,8 @@ import { fetchAuthenticatedUser, walletTypes } from "../Redux/actionCreators/Use import PaypalPaymentMgmtStrategy from "./PaypalPaymentMgmtStrategy"; import { store } from "../"; import ProxyPaymentChannelManagementStrategy from "./ProxyPaymentChannelManagementStrategy"; -import { localStorageKeys } from "../components/Hooks/useLocalStorage"; +import FreeCallPaymentStrategy from "snet-sdk-web/paymentStrategies/FreeCallPaymentStrategy"; +import { getFreeCallSign } from "../Redux/actionCreators/ServiceDetailsActions"; const DEFAULT_GAS_PRICE = 4700000; const DEFAULT_GAS_LIMIT = 210000; @@ -18,6 +20,7 @@ const EXPECTED_ID_ETHEREUM_NETWORK = Number(process.env.REACT_APP_ETH_NETWORK); let sdk; let channel; +let serviceMetadataProvider; export const callTypes = { FREE: "FREE", @@ -41,39 +44,35 @@ const parseRegularCallMetadata = ({ data }) => ({ .address, }); -const parseFreeCallMetadata = ({ data }) => { - window.localStorage.setItem(localStorageKeys.FREE_CALL_TOKEN, JSON.stringify(data.freecall_token)); - return { - "snet-payment-type": data.payment_type, - "snet-free-call-user-id": data.user_id, - "snet-current-block-number": data.block_number, - "snet-payment-channel-signature-bin": parseSignature(data.signature), - "snet-free-call-auth-token-bin": parseSignature(data.freecall_token), - "snet-payment-mpe-address": - MPEContract[process.env.REACT_APP_ETH_NETWORK][process.env.REACT_APP_TOKEN_NAME][process.env.REACT_APP_STAND] - .address, +const parseFreeCallMetadata = (data) => { + const MetadataGenerator = new FreecallMetadataGenerator(); + const metadataFields = { + type: "free-call", + userAddress: data.signerAddress, + currentBlockNumber: data.currentBlockNumber, + freecallAuthToken: hexStringToBytes(data.freeCallToken), + signatureBytes: hexStringToBytes(data.signature), + userId: data.userId, }; + return MetadataGenerator.generateMetadata(metadataFields); }; -const metadataGenerator = (serviceRequestErrorHandler, groupId) => async (serviceClient, serviceName, method) => { +export const createFreecallStrategy = async (org_id, service_id, group_name, options) => { + if (!serviceMetadataProvider) { + await createMetadataProvider(org_id, service_id, group_name, options); + } + + const paymentStrategy = new FreeCallPaymentStrategy(sdk.account, serviceMetadataProvider); + return paymentStrategy; +}; + +const metadataGenerator = (serviceRequestErrorHandler, groupId) => async (serviceClient) => { try { - const { orgId: org_id, serviceId: service_id } = serviceClient.metadata; - const { email, token } = await store.dispatch(fetchAuthenticatedUser()); - const freeCallToken = window.localStorage.getItem(localStorageKeys.FREE_CALL_TOKEN); - const payload = { - org_id, - service_id, - service_name: serviceName, - method, - username: email, - group_id: groupId, - freecall_token: freeCallToken, - }; - const apiName = APIEndpoints.SIGNER_SERVICE.name; - const apiOptions = initializeAPIOptions(token, payload); - const meta = await postAPI(apiName, APIPaths.SIGNER_FREE_CALL, apiOptions); + const { orgId, serviceId } = serviceClient.metadataProvider.serviceMetadata; + const meta = await store.dispatch(getFreeCallSign(orgId, serviceId, groupId)); return parseFreeCallMetadata(meta); } catch (err) { + console.error("error on generating metadata: ", err); serviceRequestErrorHandler(err); } }; @@ -158,7 +157,7 @@ class PaypalSDK extends SnetSDK { } } -export const initPaypalSdk = async (address, channelId) => { +export const initPaypalSdk = (address, channelId) => { const config = { networkId: process.env.REACT_APP_ETH_NETWORK, web3Provider: process.env.REACT_APP_WEB3_PROVIDER, @@ -204,6 +203,10 @@ const getMethodNames = (service) => { }); }; +const createMetadataProvider = async (org_id, service_id, groupName, options) => { + serviceMetadataProvider = await sdk.createServiceMetadataProvider(org_id, service_id, groupName, options); +}; + export const createServiceClient = async ( org_id, service_id, @@ -215,13 +218,16 @@ export const createServiceClient = async ( wallet ) => { const options = generateOptions(callType, wallet, serviceRequestErrorHandler, groupInfo); - const metadataProvider = await sdk.createServiceMetadataProvider(org_id, service_id, groupInfo.group_name, options); + await createMetadataProvider(org_id, service_id, groupInfo.group_name, options); let paymentChannelManagementStrategy = sdk && sdk._paymentChannelManagementStrategy; if (!(paymentChannelManagementStrategy instanceof PaypalPaymentMgmtStrategy)) { paymentChannelManagementStrategy = new ProxyPaymentChannelManagementStrategy(channel); } - const serviceClient = new ServiceClient(metadataProvider, paymentChannelManagementStrategy, options); - + const serviceClient = await sdk.createServiceClient({ + paymentStrategy: paymentChannelManagementStrategy, + serviceMetadataProvider, + options, + }); const finishServiceInteraction = () => { if (serviceRequestCompleteHandler) { serviceRequestCompleteHandler(); From 7eda2acb131abd1f492ad535ce4b39d860b5df0e Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:24:27 +0300 Subject: [PATCH 24/83] [SM-105] update get free call sign function --- .../actionCreators/ServiceDetailsActions.js | 91 ++++++++++++++++--- src/Redux/reducers/ServiceDetailsReducer.js | 20 +--- src/config/APIEndpoints.js | 3 - 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 2e95241c4..44666d19e 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -1,20 +1,27 @@ import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; -import { getAPI, initializeAPIOptions } from "../../utility/API"; +import { initializeAPIOptions, postAPI } from "../../utility/API"; import { fetchAuthenticatedUser } from "./UserActions"; import { loaderActions } from "./"; import { LoaderContent } from "../../utility/constants/LoaderContent"; import { isEmpty } from "lodash"; import { resetCurrentModelDetails, resetModelList } from "./ServiceTrainingActions"; +import { createFreecallStrategy } from "../../utility/sdk"; +import { initializingSdk } from "./SDKActions"; export const UPDATE_SERVICE_DETAILS = "UPDATE_SERVICE_DETAILS"; export const RESET_SERVICE_DETAILS = "RESET_SERVICE_DETAILS"; export const UPDATE_FREE_CALLS_INFO = "UPDATE_FREE_CALLS_INFO"; export const UPDATE_TRAINING_DETAILS = "UPDATE_TRAINING_DETAILS"; +export const UPDATE_FREECALL_SIGNATURE = "UPDATE_FREECALL_SIGNATURE"; const resetServiceDetails = (dispatch) => { dispatch({ type: RESET_SERVICE_DETAILS }); }; +const setFreeCallSignature = (freeCallSignature) => (dispatch) => { + dispatch({ type: UPDATE_FREECALL_SIGNATURE, payload: freeCallSignature }); +}; + const fetchServiceDetailsFailure = (err) => (dispatch) => { dispatch(loaderActions.stopAppLoader()); }; @@ -48,12 +55,12 @@ export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { } }; -const fetchMeteringDataSuccess = (usageData) => (dispatch) => { +const fetchMeteringDataSuccess = (freeCallsAvailable, freeCallsTotal) => (dispatch) => { dispatch({ type: UPDATE_FREE_CALLS_INFO, payload: { - freeCallsTotal: usageData.free_calls_total, - freeCallsAvailable: usageData.free_calls_available, + freeCallsTotal: freeCallsAvailable, + freeCallsAvailable: freeCallsTotal, }, }); }; @@ -78,21 +85,75 @@ export const fetchTrainingModel = (orgId, serviceId) => async (dispatch) => { dispatch(fetchTrainingModelSuccess(serviceTrainingData)); }; -const meteringAPI = (token, orgId, serviceId, groupId, freeCallToken) => { - const apiName = APIEndpoints.SIGNER_SERVICE.name; - const apiPath = APIPaths.FREE_CALL_USAGE; - const queryParams = { org_id: orgId, service_id: serviceId, group_id: groupId, freecall_token: freeCallToken }; - const apiOptions = initializeAPIOptions(token, null, queryParams); - return getAPI(apiName, apiPath, apiOptions); +const getAvailableFreeCalls = (orgId, serviceId, groupId) => async (dispatch) => { + const { + signature, + currentBlockNumber, + userId, + freeCallToken, + signerAddress: address, + } = await dispatch(getFreeCallSign(orgId, serviceId, groupId)); + + try { + const freecallStrategy = await createFreecallStrategy(orgId, serviceId); + const availableFreeCalls = await freecallStrategy.getFreeCallsAvailable({ + signature, + currentBlockNumber, + userId, + token: freeCallToken, + address, + }); + return availableFreeCalls; + } catch (err) { + console.error("error on getting available free calls:", err); + + throw new Error(err); + } +}; + +export const getFreeCallSign = (orgId, serviceId, groupId) => async (dispatch, getState) => { + try { + const sdk = await dispatch(initializingSdk()); + const currentBlock = await sdk.account.getCurrentBlockNumber(); + const { email, token } = await dispatch(fetchAuthenticatedUser()); + console.log(getState()); + + const freeCallSignatureDetails = getState().serviceDetailsReducer.freeCallSignature; + + if (freeCallSignatureDetails.expirationBlock && freeCallSignatureDetails.expirationBlock < currentBlock) { + return { ...freeCallSignatureDetails, userId: email }; + } + const payload = { + organization_id: orgId, + service_id: serviceId, + group_id: groupId, + }; + const apiName = APIEndpoints.SIGNER_SERVICE.name; + const apiOptions = initializeAPIOptions(token, payload); + const { data: freeCallSignerResp } = await postAPI(apiName, APIPaths.SIGNER_FREE_CALL, apiOptions); + + const freeCallSignature = { + signature: freeCallSignerResp.signature_hex, + expirationBlock: freeCallSignerResp.expiration_block_number, + currentBlockNumber: freeCallSignerResp.current_block_number, + freeCallToken: freeCallSignerResp.free_call_token_hex, + signerAddress: freeCallSignerResp.signer_address, + userId: email, + }; + dispatch(setFreeCallSignature(freeCallSignature)); + return freeCallSignature; + } catch (err) { + console.error(err); + throw err; + } }; export const fetchMeteringData = - ({ orgId, serviceId, groupId, freeCallToken }) => + ({ orgId, serviceId, groupId, freeCallsTotal }) => async (dispatch) => { - const { email, token } = await dispatch(fetchAuthenticatedUser()); - const usageData = await meteringAPI(token, orgId, serviceId, groupId, email, freeCallToken); - dispatch(fetchMeteringDataSuccess(usageData)); - return usageData; + const freeCallsAvailable = await dispatch(getAvailableFreeCalls(orgId, serviceId, groupId)); + dispatch(fetchMeteringDataSuccess(freeCallsAvailable, freeCallsTotal)); + return { freeCallsAvailable, freeCallsTotal }; }; export const getIsTrainingAvailable = (detailsTraining, isLoggedIn) => { diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index 42ea4533d..b3b78dbd2 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -5,11 +5,12 @@ import some from "lodash/some"; import map from "lodash/map"; const InitialServiceDetails = { - details: {}, freeCalls: { freeCallsTotal: "", freeCallsAvailable: "", }, + freeCallSignature: { signature: "", expirationBlock: "", freeCallToken: "", signerAddress: "" }, + details: {}, detailsTraining: {}, }; @@ -27,26 +28,15 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { case serviceDetailsActions.UPDATE_TRAINING_DETAILS: { return { ...state, detailsTraining: action.payload }; } + case serviceDetailsActions.UPDATE_FREECALL_SIGNATURE: { + return { ...state, freeCallSignature: action.payload }; + } default: { return state; } } }; -// export const freeCalls = (state) => { -// const selectedGroup = groupInfo(state); -// if (!selectedGroup) { -// return {}; -// } -// if (selectedGroup.free_calls === 0) { -// return { allowed: 0, remaining: 0 }; -// } -// return { -// allowed: selectedGroup.free_calls, -// remaining: selectedGroup.free_calls - state.serviceDetailsReducer.freeCallsUsed, -// }; -// }; - export const currentServiceDetails = (state) => { return state.serviceDetailsReducer.details; }; diff --git a/src/config/APIEndpoints.js b/src/config/APIEndpoints.js index a8b40727c..d3fc75e77 100644 --- a/src/config/APIEndpoints.js +++ b/src/config/APIEndpoints.js @@ -36,7 +36,6 @@ export const APIPaths = { GET_USER_PROFILE: "/profile", UPDATE_USER_PROFILE: "/profile", DELETE_USER: "/user/delete", - FREE_CALL_USAGE: "/freecall/usage", FEEDBACK: "/feedback", GET_SERVICE_LIST: "/service", FILTER_DATA: "/service?attribute=", @@ -44,10 +43,8 @@ export const APIPaths = { GET_CAROUSEL: "/uicontent/marketplacecarousel", UPDATE_CHANNEL_BALANCE: (channelId) => `/channel/${channelId}/balance`, LINKED_PROVIDERS: "/v2/channel", - FREE_CALL_TOKEN: "/free-call/token", SIGNER_FREE_CALL: "/freecall", SIGNER_STATE_SERVICE: "/state-service", - GET_SIGNATURE: "/sign-call", SIGNER_REGULAR_CALL: "/regular-call", ORDERS_LIST: "/order", ORDER_DETAILS: "/order", From 23ec781f7d67ab23a53fd3c4e6a6d049fceaa2ed Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:26:53 +0300 Subject: [PATCH 25/83] [SM-43] fix eslint errors --- src/components/AiMarketplace/MainSection/Filter/index.js | 1 + src/utility/ProxyPaymentChannelManagementStrategy.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AiMarketplace/MainSection/Filter/index.js b/src/components/AiMarketplace/MainSection/Filter/index.js index 4ff8d79ba..286185071 100644 --- a/src/components/AiMarketplace/MainSection/Filter/index.js +++ b/src/components/AiMarketplace/MainSection/Filter/index.js @@ -21,6 +21,7 @@ const Filter = ({ listView, total_count, handleSearchChange, toggleView, current useEffect(() => { return () => dispatch(serviceActions.resetFilter({ pagination })); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [dispatch]); const handleSearch = (event) => { diff --git a/src/utility/ProxyPaymentChannelManagementStrategy.js b/src/utility/ProxyPaymentChannelManagementStrategy.js index 0d749b93b..d7e63a71f 100644 --- a/src/utility/ProxyPaymentChannelManagementStrategy.js +++ b/src/utility/ProxyPaymentChannelManagementStrategy.js @@ -34,7 +34,6 @@ export default class ProxyPaymentChannelManagementStrategy { async getPaymentMetadata(serviceClient) { const channel = await this.selectChannel(); - const amount = channel?.state?.currentSignedAmount.toNumber() + serviceClient._pricePerServiceCall.toNumber(); const signature = await this.generateSignature(serviceClient, channel.channelId, channel.state.nonce, amount); From 6bd2ae48610c0c4d02332b35a5da11214c21ac72 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:27:50 +0300 Subject: [PATCH 26/83] [SM-43] fix unused async and dispatch expressions --- src/Redux/actionCreators/DatasetActions.js | 8 ++++---- src/Redux/actionCreators/ServiceTrainingActions.js | 2 +- .../DataPreset/DashboardModal/ButtonGroup.js | 2 +- src/components/ServiceDetails/DataPreset/index.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Redux/actionCreators/DatasetActions.js b/src/Redux/actionCreators/DatasetActions.js index 92acfb48d..c80c78fca 100644 --- a/src/Redux/actionCreators/DatasetActions.js +++ b/src/Redux/actionCreators/DatasetActions.js @@ -55,12 +55,12 @@ export const clearRecentDatasets = () => (dispatch) => { }); }; -export const getDatasetStatistic = (datasetKey) => async (dispatch) => { +export const getDatasetStatistic = (datasetKey) => { const params = new URLSearchParams([["dataset_key", datasetKey]]); return DatasetClient.get(DatasetEndpoints.VALIDATE_AND_ANALIZE, { params }); }; -export const improveDataset = (datasetKey, improveOptionsList) => async (dispatch) => { +export const improveDataset = (datasetKey, improveOptionsList) => { const params = { dataset_key: datasetKey, improve_options: improveOptionsList.reduce((acc, field) => { @@ -71,7 +71,7 @@ export const improveDataset = (datasetKey, improveOptionsList) => async (dispatc return DatasetClient.post(DatasetEndpoints.IMPROVE, params); }; -export const mergeDatasets = (mainDataset, mergeDataset) => async (dispatch) => { +export const mergeDatasets = (mainDataset, mergeDataset) => { const params = { dataset_key_base: mainDataset, dataset_key_additional: mergeDataset, @@ -85,7 +85,7 @@ export const mergeDatasets = (mainDataset, mergeDataset) => async (dispatch) => }); }; -export const validateMergeDatasets = (mainDataset, mergeDataset) => async (dispatch) => { +export const validateMergeDatasets = (mainDataset, mergeDataset) => { const params = { dataset_key_base: mainDataset, dataset_key_additional: mergeDataset, diff --git a/src/Redux/actionCreators/ServiceTrainingActions.js b/src/Redux/actionCreators/ServiceTrainingActions.js index 929774d0c..12b069a80 100644 --- a/src/Redux/actionCreators/ServiceTrainingActions.js +++ b/src/Redux/actionCreators/ServiceTrainingActions.js @@ -229,7 +229,7 @@ export const publishFilesToS3 = async (fileBlob, name, S3Instance, folder, email } }; -export const getDatasetSizeFromS3 = async (fileKey, S3instance) => { +export const getDatasetSizeFromS3 = (fileKey, S3instance) => { return S3instance.get(filesToS3Endpoints.DOWNLOAD, { params: { key: fileKey, action: "getsize" } }) .then((response) => { return response.data.fileSize; diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js b/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js index ccd1cdec6..0b88d87a3 100644 --- a/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js @@ -19,7 +19,7 @@ const ButtonsGroup = ({ classes, selectedParameters, isTableView, toggleTableVie const getImprovedDataset = async () => { try { dispatch(startAppLoader(LoaderContent.IMPROVE_DATASET)); - const { data } = await dispatch(improveDataset(dataset.datasetKey, Array.from(selectedParameters.keys()))); + const { data } = improveDataset(dataset.datasetKey, Array.from(selectedParameters.keys())); const size = await getDatasetSizeFromS3(data.dataset_key_new, DatafactoryInstanceS3); const improvedDataset = { additionalInfo: { diff --git a/src/components/ServiceDetails/DataPreset/index.js b/src/components/ServiceDetails/DataPreset/index.js index 305688da0..56e9cd4ed 100644 --- a/src/components/ServiceDetails/DataPreset/index.js +++ b/src/components/ServiceDetails/DataPreset/index.js @@ -38,7 +38,7 @@ const DataPreset = ({ classes }) => { try { dispatch(loaderActions.startAppLoader(LoaderContent.GET_DATASET_STATISTIC)); const datasetKey = dataset?.datasetKey; - const { data } = await dispatch(getDatasetStatistic(datasetKey)); + const { data } = getDatasetStatistic(datasetKey); const enrichedDataset = { ...dataset, additionalInfo: data }; const actualDatasetIndexInRecent = recentDatasets.find((el) => el.datasetKey === datasetKey); if (!actualDatasetIndexInRecent) { @@ -91,7 +91,7 @@ const DataPreset = ({ classes }) => { const onMergeDatasets = async () => { try { dispatch(startAppLoader(LoaderContent.MERGE_DATASETS)); - const mergedDatasets = await dispatch(validateMergeDatasets(mainDataset?.datasetKey, mergeDataset?.datasetKey)); + const mergedDatasets = validateMergeDatasets(mainDataset?.datasetKey, mergeDataset?.datasetKey); const size = await getDatasetSizeFromS3(mergedDatasets.dataset_key_merged, DatafactoryInstanceS3); const mergedDataset = { additionalInfo: mergedDatasets, From 67957870fa389ad0e2413bc7f59a2ffab39af771 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 16 Jun 2025 13:28:39 +0300 Subject: [PATCH 27/83] [SM-43] update export of get sdk --- .github/workflows/sonar.yml | 26 ++++++++++++------- src/Redux/actionCreators/SDKActions.js | 2 ++ src/Redux/actionCreators/ServiceActions.js | 6 ++--- src/Redux/actionCreators/UserActions.js | 5 ++-- .../TokenPurchase/TokenPurchase.jsx | 4 +-- .../MetamaskDetails/MetamaskDetails.js | 5 ++-- .../UserProfile/UserProfileAccount/index.js | 2 +- 7 files changed, 30 insertions(+), 20 deletions(-) diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index eb0506080..0669e954d 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -1,18 +1,24 @@ -on: + +name: Sonar Analysis Workflow + +on: push: branches: - master - development + pull_request: + types: [opened, synchronize, reopened] -name: Sonar Analysis Workflow jobs: - sonarQubeTrigger: - name: SonarQube Trigger + build: + name: Build and analyze runs-on: ubuntu-latest + steps: - - uses: actions/checkout@master - - name: SonarQube Scan - uses: kitabisa/sonarqube-action@master - with: - host: ${{ secrets.SONAR_HOST }} - login: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} diff --git a/src/Redux/actionCreators/SDKActions.js b/src/Redux/actionCreators/SDKActions.js index dd3ebb3d8..ccfac3087 100644 --- a/src/Redux/actionCreators/SDKActions.js +++ b/src/Redux/actionCreators/SDKActions.js @@ -18,6 +18,8 @@ export const initializingSdk = () => async (dispatch) => { dispatch(updateSdkInstance(sdk)); return sdk; } catch (error) { + console.error("init sdk", error); + throw new Error(error); } }; diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index b181e446b..58e8e2adb 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -112,7 +112,7 @@ export const resetFilter = .catch(() => dispatch(loaderActions.stopAIServiceListLoader())); }; -const fetchFeedbackAPI = (email, orgId, serviceId, token) => { +const fetchFeedbackAPI = (orgId, serviceId, token) => { const apiName = APIEndpoints.USER.name; const path = `${APIPaths.FEEDBACK}?org_id=${orgId}&service_id=${serviceId}`; const apiOptions = initializeAPIOptions(token); @@ -157,8 +157,8 @@ export const downloadAuthToken = (serviceId, groupId, publicKey, orgId) => async //Username review export const fetchFeedback = (orgId, serviceId) => async (dispatch) => { - const { email, token } = await dispatch(userActions.fetchAuthenticatedUser()); - return fetchFeedbackAPI(email, orgId, serviceId, token); + const { token } = await dispatch(userActions.fetchAuthenticatedUser()); + return fetchFeedbackAPI(orgId, serviceId, token); }; const submitFeedbackAPI = (feedbackObj, token) => { diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index 5edceb678..b1bf22e7b 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -11,10 +11,11 @@ import moment from "moment"; import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; import { parseError } from "../../utility/ErrorHandling"; -import { sdkActions, errorActions, loaderActions } from "./"; +import { errorActions, loaderActions } from "./"; import { LoaderContent } from "../../utility/constants/LoaderContent"; import { getAPI, initializeAPIOptions, postAPI } from "../../utility/API"; import Routes from "../../utility/constants/Routes"; +import { getSdk } from "./SDKActions"; export const SET_USER_DETAILS = "SET_USER_DETAILS"; export const LOGIN_SUCCESS = "LOGIN_SUCCESS"; @@ -583,7 +584,7 @@ export const registerWallet = (address, type) => async (dispatch) => { export const updateMetamaskWallet = () => async (dispatch, getState) => { try { - const sdk = await dispatch(sdkActions.getSdk()); + const sdk = await dispatch(getSdk()); const address = await sdk.account.getAddress(); if (getState().userReducer.wallet?.address === address) { diff --git a/src/components/TokenPurchase/TokenPurchase.jsx b/src/components/TokenPurchase/TokenPurchase.jsx index dac17f6f2..ed54b74d2 100644 --- a/src/components/TokenPurchase/TokenPurchase.jsx +++ b/src/components/TokenPurchase/TokenPurchase.jsx @@ -4,7 +4,7 @@ import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; import StyledButton from "../common/StyledButton"; import { useDispatch } from "react-redux"; -import { sdkActions } from "../../Redux/actionCreators"; +import { getSdk } from "../../Redux/actionCreators/SDKActions"; const TokenPurchase = () => { const dispatch = useDispatch(); @@ -16,7 +16,7 @@ const TokenPurchase = () => { return; } try { - const sdk = await dispatch(sdkActions.getSdk()); + const sdk = await dispatch(getSdk()); const address = await sdk.account.getAddress(); const onrampInstance = new OnrampWebSDK({ diff --git a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js index 40903d586..fdfae3568 100644 --- a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js +++ b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MetamaskDetails.js @@ -4,10 +4,11 @@ import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; import { cogsToToken } from "../../../../utility/PricingStrategy"; -import { loaderActions, sdkActions } from "../../../../Redux/actionCreators"; +import { loaderActions } from "../../../../Redux/actionCreators"; import { LoaderContent } from "../../../../utility/constants/LoaderContent"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; import { Networks } from "../../../../config/Networks"; +import { getSdk } from "../../../../Redux/actionCreators/SDKActions"; const MetamaskDetails = ({ classes }) => { const wallet = useSelector((state) => state.userReducer.wallet); @@ -21,7 +22,7 @@ const MetamaskDetails = ({ classes }) => { const retrieveAccountDetails = useCallback(async () => { try { dispatch(loaderActions.startAppLoader(LoaderContent.FETCH_MM_ACC_DETAILS)); - const sdk = await dispatch(sdkActions.getSdk()); + const sdk = await dispatch(getSdk()); const escrowBalance = await sdk.account.escrowBalance(); const tokenBalance = await sdk.account.balance(); diff --git a/src/components/UserProfile/UserProfileAccount/index.js b/src/components/UserProfile/UserProfileAccount/index.js index a5bb29a07..5214de019 100644 --- a/src/components/UserProfile/UserProfileAccount/index.js +++ b/src/components/UserProfile/UserProfileAccount/index.js @@ -77,7 +77,7 @@ const UserProfileAccount = ({ classes }) => { setCurrentAddress(""); setWallets([]); } - }, [getWallets]); + }, [getWallets, dispatch]); useEffect(() => { const ethereumProvider = getEthereumProvider(); From 7b925cb0a486894fcac3505773e0842c378ef4b9 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:03:38 +0300 Subject: [PATCH 28/83] [SM-43] update export of create payment strategy --- src/Redux/actionCreators/SDKActions.js | 11 +++++++++++ .../ServiceDemo/CompletedActions/index.js | 7 ++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Redux/actionCreators/SDKActions.js b/src/Redux/actionCreators/SDKActions.js index ccfac3087..d24254a9c 100644 --- a/src/Redux/actionCreators/SDKActions.js +++ b/src/Redux/actionCreators/SDKActions.js @@ -1,5 +1,6 @@ import { isEmpty } from "lodash"; import { initSdk } from "../../utility/sdk"; +import PaymentChannelManagement from "../../utility/PaymentChannelManagement"; export const SET_SDK = "SET_SDK"; export const SET_SERVICE_CLIENT = "SET_SERVICE_CLIENT"; @@ -48,3 +49,13 @@ export const getServiceClient = (organizationId, serviceId) => async (dispatch, serviceClient = await dispatch(initializeServiceClient(organizationId, serviceId)); return serviceClient; }; + +export const createPaymentChannelManagement = (orgId, serviceId) => async (dispatch) => { + try { + const sdk = await dispatch(initializingSdk()); + const metadataProvider = await sdk.createServiceMetadataProvider(orgId, serviceId); + return new PaymentChannelManagement(sdk, metadataProvider); + } catch (err) { + console.error(err); + } +}; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js index 9c7351581..6f10d30e9 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/index.js @@ -3,9 +3,8 @@ import React, { useCallback, useEffect, useState } from "react"; import StyledButton from "../../../../common/StyledButton"; import { useStyles } from "./styles"; import UserFeedback from "../UserFeedback"; -import PaymentChannelManagement from "../../../../../utility/PaymentChannelManagement"; import { updateChannelBalanceAPI, walletTypes } from "../../../../../Redux/actionCreators/UserActions"; -import { getSdk } from "../../../../../Redux/actionCreators/SDKActions"; +import { createPaymentChannelManagement } from "../../../../../Redux/actionCreators/SDKActions"; import { channelInfo as getChannelInfo } from "../../../../../Redux/reducers/UserReducer"; import { useDispatch, useSelector } from "react-redux"; import { callTypes } from "../../../../../utility/sdk"; @@ -31,9 +30,7 @@ const CompletedActions = ({ isComplete, callType, feedback, orgId, serviceId, re }; const getSignedAmountAndChannelId = useCallback(async () => { - const sdk = await dispatch(getSdk()); - const serviceClient = await sdk.createServiceClient({ orgId, serviceId }); - const paymentChannelManagement = new PaymentChannelManagement(sdk, serviceClient); + const paymentChannelManagement = await dispatch(createPaymentChannelManagement(orgId, serviceId)); await paymentChannelManagement.updateChannelInfo(); const channel = paymentChannelManagement._channel; // eslint-disable-next-line no-undef From c038b5a20cd0962eccdf2212439f7776d236c77a Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:06:52 +0300 Subject: [PATCH 29/83] [SM-103] update payment channel managment --- src/utility/PaymentChannelManagement.js | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/utility/PaymentChannelManagement.js b/src/utility/PaymentChannelManagement.js index ac15dc73c..54b9aca83 100644 --- a/src/utility/PaymentChannelManagement.js +++ b/src/utility/PaymentChannelManagement.js @@ -1,36 +1,41 @@ import find from "lodash/find"; import minBy from "lodash/minBy"; import isEmpty from "lodash/isEmpty"; -import { updateChannel } from "./sdk"; +import { PaymentChannelProvider } from "snet-sdk-core/mpe"; const ONE_YEAR_BLOCKS = 2102400; export default class PaymentChannelManagement { - constructor(sdkContext, serviceClient) { + constructor(sdkContext, metadataProvider) { this._sdkContext = sdkContext; - this._serviceClient = serviceClient; + this._metadataProvider = metadataProvider; this._channel = undefined; + this.paymentChannelProvider = undefined; } get channel() { return this._channel; } - get serviceClient() { - return this._serviceClient; + get metadataProvider() { + return this._metadataProvider; } async updateChannelInfo() { try { - const channels = await this.serviceClient.loadOpenChannels(); + console.log(this._sdkContext); + + this.paymentChannelProvider = new PaymentChannelProvider(this._sdkContext._account, this._metadataProvider); + console.log(this.paymentChannelProvider); + const channels = await this.paymentChannelProvider.loadOpenChannels(); if (isEmpty(channels)) { return; } this._channel = minBy(channels, ({ channelId }) => channelId); - updateChannel(this._channel); await this._channel.syncState(); } catch (error) { + console.error("update channel: ", error); throw new Error(error); } } @@ -39,7 +44,7 @@ export default class PaymentChannelManagement { const serviceCallPrice = this.noOfCallsToCogs(noOfServiceCalls); const defaultExpiration = await this._channelExtensionBlockNumber(); - this._channel = await this.serviceClient.openChannel(serviceCallPrice, defaultExpiration); + this._channel = await this.paymentChannelProvider.openChannel(serviceCallPrice, defaultExpiration); await this._channel.syncState(); this._sdkContext.currentChannel = this._channel; } @@ -100,7 +105,7 @@ export default class PaymentChannelManagement { } _pricePerServiceCall() { - const { pricing } = this.serviceClient.group; + const { pricing } = this._metadataProvider.group; const fixedPricing = find(pricing, ({ price_model }) => "fixed_price" === price_model); return fixedPricing.price_in_cogs; @@ -112,13 +117,13 @@ export default class PaymentChannelManagement { const channelExpiryBlock = this._channel?.state?.expiry ?? 0; const defaultExpiration = - Number(currentBlockNumber) + this.serviceClient.group.payment_expiration_threshold + ONE_YEAR_BLOCKS; + Number(currentBlockNumber) + this._metadataProvider.group.payment_expiration_threshold + ONE_YEAR_BLOCKS; return channelExpiryBlock < defaultExpiration ? defaultExpiration : channelExpiryBlock; } async _defaultChannelExpiration() { const currentBlockNumber = await this._sdkContext.web3.eth.getBlockNumber(); - return currentBlockNumber + this._serviceClient.group.payment_expiration_threshold; + return currentBlockNumber + this._metadataProvider.group.payment_expiration_threshold; } } From 2cd8031afbef5b6b2e0dfc938e84c1fbd787fc51 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:07:48 +0300 Subject: [PATCH 30/83] [SM-105] fix free calls total and available switch --- src/Redux/actionCreators/ServiceDetailsActions.js | 8 ++++---- src/Redux/reducers/ServiceDetailsReducer.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 44666d19e..b5936eb62 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -59,8 +59,8 @@ const fetchMeteringDataSuccess = (freeCallsAvailable, freeCallsTotal) => (dispat dispatch({ type: UPDATE_FREE_CALLS_INFO, payload: { - freeCallsTotal: freeCallsAvailable, - freeCallsAvailable: freeCallsTotal, + freeCallsTotal, + freeCallsAvailable, }, }); }; @@ -116,8 +116,6 @@ export const getFreeCallSign = (orgId, serviceId, groupId) => async (dispatch, g const sdk = await dispatch(initializingSdk()); const currentBlock = await sdk.account.getCurrentBlockNumber(); const { email, token } = await dispatch(fetchAuthenticatedUser()); - console.log(getState()); - const freeCallSignatureDetails = getState().serviceDetailsReducer.freeCallSignature; if (freeCallSignatureDetails.expirationBlock && freeCallSignatureDetails.expirationBlock < currentBlock) { @@ -152,6 +150,8 @@ export const fetchMeteringData = ({ orgId, serviceId, groupId, freeCallsTotal }) => async (dispatch) => { const freeCallsAvailable = await dispatch(getAvailableFreeCalls(orgId, serviceId, groupId)); + console.log("fetchMeteringData freeCallsAvailable: ", freeCallsAvailable); + dispatch(fetchMeteringDataSuccess(freeCallsAvailable, freeCallsTotal)); return { freeCallsAvailable, freeCallsTotal }; }; diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index b3b78dbd2..129805f85 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -6,8 +6,8 @@ import map from "lodash/map"; const InitialServiceDetails = { freeCalls: { - freeCallsTotal: "", - freeCallsAvailable: "", + freeCallsTotal: 0, + freeCallsAvailable: 0, }, freeCallSignature: { signature: "", expirationBlock: "", freeCallToken: "", signerAddress: "" }, details: {}, From 82d6b3687d9d441be82df0b45f705b9d653ea1e5 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:08:30 +0300 Subject: [PATCH 31/83] [SM-103] remove proxy payment startegy --- .../ProxyPaymentChannelManagementStrategy.js | 58 ------------------- src/utility/sdk.js | 18 +++--- 2 files changed, 7 insertions(+), 69 deletions(-) delete mode 100644 src/utility/ProxyPaymentChannelManagementStrategy.js diff --git a/src/utility/ProxyPaymentChannelManagementStrategy.js b/src/utility/ProxyPaymentChannelManagementStrategy.js deleted file mode 100644 index d7e63a71f..000000000 --- a/src/utility/ProxyPaymentChannelManagementStrategy.js +++ /dev/null @@ -1,58 +0,0 @@ -export default class ProxyPaymentChannelManagementStrategy { - constructor(channel) { - this._channel = channel; - } - - selectChannel() { - return this._channel; - } - - generateSignature(serviceClient, channelId, nonce, amount) { - return serviceClient.signData( - { - t: "string", - v: "__MPE_claim_message", - }, - { - t: "address", - v: serviceClient.mpeContract.address, - }, - { - t: "uint256", - v: channelId, - }, - { - t: "uint256", - v: nonce, - }, - { - t: "uint256", - v: amount, - } - ); - } - - async getPaymentMetadata(serviceClient) { - const channel = await this.selectChannel(); - const amount = channel?.state?.currentSignedAmount.toNumber() + serviceClient._pricePerServiceCall.toNumber(); - - const signature = await this.generateSignature(serviceClient, channel.channelId, channel.state.nonce, amount); - return [ - { - "snet-payment-type": "escrow", - }, - { - "snet-payment-channel-id": `${channel.channelId}`, - }, - { - "snet-payment-channel-nonce": `${channel.state.nonce}`, - }, - { - "snet-payment-channel-amount": `${amount}`, - }, - { - "snet-payment-channel-signature-bin": signature.toString("base64"), - }, - ]; - } -} diff --git a/src/utility/sdk.js b/src/utility/sdk.js index e03330e08..67ff15b8e 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -7,7 +7,6 @@ import { initializeAPIOptions, postAPI } from "./API"; import { fetchAuthenticatedUser, walletTypes } from "../Redux/actionCreators/UserActions"; import PaypalPaymentMgmtStrategy from "./PaypalPaymentMgmtStrategy"; import { store } from "../"; -import ProxyPaymentChannelManagementStrategy from "./ProxyPaymentChannelManagementStrategy"; import FreeCallPaymentStrategy from "snet-sdk-web/paymentStrategies/FreeCallPaymentStrategy"; import { getFreeCallSign } from "../Redux/actionCreators/ServiceDetailsActions"; @@ -19,7 +18,6 @@ export const ON_NETWORK_CHANGE = "chainChanged"; const EXPECTED_ID_ETHEREUM_NETWORK = Number(process.env.REACT_APP_ETH_NETWORK); let sdk; -let channel; let serviceMetadataProvider; export const callTypes = { @@ -112,6 +110,8 @@ const paidCallMetadataGenerator = (serviceRequestErrorHandler) => async (channel }; const generateOptions = (callType, wallet, serviceRequestErrorHandler, groupInfo) => { + console.log("callType: ", callType); + const defaultOptions = { concurrency: false }; if (process.env.REACT_APP_SANDBOX) { return { @@ -123,6 +123,8 @@ const generateOptions = (callType, wallet, serviceRequestErrorHandler, groupInfo if (callType === callTypes.FREE) { return { ...defaultOptions, metadataGenerator: metadataGenerator(serviceRequestErrorHandler, groupInfo.group_id) }; } + console.log("wallet: ", wallet); + if (wallet && wallet.type === walletTypes.METAMASK) { return { ...defaultOptions }; } @@ -171,10 +173,6 @@ export const initPaypalSdk = (address, channelId) => { return sdk; }; -export const updateChannel = (newChannel) => { - channel = newChannel; -}; - export const initSdk = async () => { if (sdk && !(sdk instanceof PaypalSDK)) { return Promise.resolve(sdk); @@ -217,14 +215,12 @@ export const createServiceClient = async ( callType, wallet ) => { + console.log("createServiceClient"); + const options = generateOptions(callType, wallet, serviceRequestErrorHandler, groupInfo); + console.log("createServiceClient ", options); await createMetadataProvider(org_id, service_id, groupInfo.group_name, options); - let paymentChannelManagementStrategy = sdk && sdk._paymentChannelManagementStrategy; - if (!(paymentChannelManagementStrategy instanceof PaypalPaymentMgmtStrategy)) { - paymentChannelManagementStrategy = new ProxyPaymentChannelManagementStrategy(channel); - } const serviceClient = await sdk.createServiceClient({ - paymentStrategy: paymentChannelManagementStrategy, serviceMetadataProvider, options, }); From cb5e390edb4212b08432abc18c8571783b0e7208 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:10:04 +0300 Subject: [PATCH 32/83] [SM-105] update metamask flow --- .../MetamaskFlow/ContinueButton.jsx | 39 ++ .../MetamaskFlow/DepositButton.jsx | 25 ++ .../MetamaskFlow/PaymentOptions.jsx | 118 ++++++ .../ExpiredSession/MetamaskFlow/helpers.js | 19 + .../ExpiredSession/MetamaskFlow/index.js | 336 +++++------------- .../ExpiredSession/MetamaskFlow/metadata.js | 26 ++ .../ExpiredSession/MetamaskFlow/style.js | 9 +- .../Purchase/ExpiredSession/styles.js | 6 +- .../AboutService/ServiceDemo/styles.js | 3 + 9 files changed, 332 insertions(+), 249 deletions(-) create mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx create mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/DepositButton.jsx create mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx create mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/helpers.js create mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/metadata.js diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx new file mode 100644 index 000000000..a1a4f222c --- /dev/null +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx @@ -0,0 +1,39 @@ +import { memo } from "react"; +import { withStyles } from "@mui/styles"; +import { useStyles } from "./style"; +import Tooltip from "@mui/material/Tooltip"; +import PropTypes from "prop-types"; +import StyledButton from "../../../../../../common/StyledButton"; + +const ContinueButton = ({classes, isServiceAvailable, isContinueEnabled, handleSubmit}) => { + const tooltipText = "Service is currently offline. Please try after sometime"; + const showTooltip = !isServiceAvailable; + const isDisabled = !isServiceAvailable || !isContinueEnabled; +console.log("ContinueButton: ", isServiceAvailable, isContinueEnabled); + + return ( + +
+ +
+
+ ) + } + + export default memo(withStyles(useStyles)(ContinueButton)); + + ContinueButton.propTypes = { + classes: PropTypes.object.isRequired, + handleSubmit: PropTypes.func.isRequired, + isContinueEnabled: PropTypes.bool.isRequired, + isServiceAvailable: PropTypes.bool.isRequired + }; \ No newline at end of file diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/DepositButton.jsx b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/DepositButton.jsx new file mode 100644 index 000000000..953903a61 --- /dev/null +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/DepositButton.jsx @@ -0,0 +1,25 @@ +import { Fragment, memo, useState } from "react"; +import PurchaseDialog from "../../PurchaseDialog"; +import PropTypes from "prop-types"; + +const { default: StyledButton } = require("../../../../../../common/StyledButton") + +const DepositButton = ({isHighlight = false}) => { +const [showPurchaseDialog, setShowPurchaseDialog] = useState(false); + return ( + + setShowPurchaseDialog(false)} /> + setShowPurchaseDialog(true)} + /> + + ) +} + +export default memo(DepositButton); + +DepositButton.propTypes = { + isHighlight: PropTypes.bool +}; \ No newline at end of file diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx new file mode 100644 index 000000000..33a1eb40f --- /dev/null +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx @@ -0,0 +1,118 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./style"; +import { Fragment, memo, useCallback, useEffect, useMemo, useState } from "react"; +import ChannelSelectionBox from "../../ChannelSelectionBox"; +import { payTypes } from "./metadata"; +import { formatValue, isValidCallsNumber } from "./helpers"; +import PropTypes from "prop-types"; + +const PaymentOptions = ({classes, servicePrice, noOfServiceCalls, mpeBalance, totalPrice, selectedPayType, channelBalance, setSelectedPayType, setNoOfServiceCalls}) => { + const [disabledPayTypes, setDisabledPayTypes] = useState([]); + const paymentOptions = useMemo(() => ({ + [payTypes.SINGLE_CALL]: { + type: payTypes.SINGLE_CALL, + title: "Single Call", + description: "Tokens are purchsed for a single call. The tokens are purchsed from the available escrow balance.", + disabled: disabledPayTypes.includes(payTypes.SINGLE_CALL) + }, + [payTypes.MULTIPLE_CALLS]: { + type: payTypes.MULTIPLE_CALLS, + title: "Multiple Calls", + description: "Select the no of calls you want to make. The tokens are purchased from the available escrow balance. This option helps save the gas cost.", + disabled: disabledPayTypes.includes(payTypes.MULTIPLE_CALLS) + } + }), [disabledPayTypes]); + + useEffect(() => { + const pushTypeToDisable = (disabledPayTypes, type) => { + !disabledPayTypes.includes(type) && disabledPayTypes.push(type); + } + + const handleDisabledPaytypes = () => { + const disabledPayTypes = []; + + if (channelBalance <= 0) { + pushTypeToDisable(disabledPayTypes, payTypes.CHANNEL_BALANCE) + } + if (mpeBalance <= 0) { + pushTypeToDisable(disabledPayTypes, payTypes.SINGLE_CALL); + pushTypeToDisable(disabledPayTypes, payTypes.MULTIPLE_CALLS); + } + setDisabledPayTypes(disabledPayTypes); + }; + + handleDisabledPaytypes(); + }, [channelBalance, mpeBalance]); + + const handlePayTypeChange = useCallback((value) => { + if (disabledPayTypes.includes(value) || selectedPayType === value) { + return; + } + if (selectedPayType === payTypes.SINGLE_CALL) { + setNoOfServiceCalls(1); + } + setSelectedPayType(value); + }, [disabledPayTypes, selectedPayType]); + + const handleNoOfCallsChange = useCallback((event) => { + let noOfServiceCalls = event.target.value; + if (!noOfServiceCalls) { + noOfServiceCalls = 0; + } + noOfServiceCalls = formatValue(noOfServiceCalls); + setNoOfServiceCalls(noOfServiceCalls); + if (!isValidCallsNumber(noOfServiceCalls)) { + return; + } + + }, []); + + return ( + + handlePayTypeChange(payTypes.SINGLE_CALL)} + inputProps={{ + totalPrice: servicePrice, + unit: process.env.REACT_APP_TOKEN_NAME, + noInput: true, + }} + disabled={paymentOptions[payTypes.SINGLE_CALL].disabled} + /> +
+
Best Value
+ handlePayTypeChange(payTypes.MULTIPLE_CALLS)} + inputProps={{ + noOfServiceCalls, + onChange: handleNoOfCallsChange, + totalPrice, + unit: process.env.REACT_APP_TOKEN_NAME, + }} + disabled={paymentOptions[payTypes.MULTIPLE_CALLS].disabled} + /> +
+
+ ) +} + +export default memo(withStyles(useStyles)(PaymentOptions)); + +PaymentOptions.propTypes = { + classes: PropTypes.object.isRequired, + setNoOfServiceCalls: PropTypes.func.isRequired, + setSelectedPayType: PropTypes.func.isRequired, + noOfServiceCalls: PropTypes.number.isRequired, + totalPrice: PropTypes.string.isRequired, + mpeBalance: PropTypes.string.isRequired, + servicePrice: PropTypes.string.isRequired, + channelBalance: PropTypes.string.isRequired, + selectedPayType: PropTypes.oneOf(Object.keys(payTypes)).isRequired +}; \ No newline at end of file diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/helpers.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/helpers.js new file mode 100644 index 000000000..6f85b4532 --- /dev/null +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/helpers.js @@ -0,0 +1,19 @@ +import { MIN_CALLS_NUMBER } from "./metadata"; + +export const formatValue = (value) => { + let stringValue = String(value); + if (stringValue[0] === "0" && stringValue.length > 1) { + return stringValue.slice(1); + } + return value; +}; + +export const isCallsMoreOrEqualThanMinimum = (noOfServiceCalls) => { + return noOfServiceCalls >= MIN_CALLS_NUMBER; +}; + +export const isValidCallsNumber = (numberOfCalls) => { + const isInteger = numberOfCalls % 1 === 0; + const isNumber = !isNaN(Number(numberOfCalls)); + return isInteger && isNumber; +}; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index 04ec1dc2f..b396bbc02 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -1,15 +1,12 @@ -import React, { Fragment, useEffect, useState } from "react"; +import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import PropTypes from "prop-types"; import { useDispatch, useSelector } from "react-redux"; -import Tooltip from "@mui/material/Tooltip"; import { withStyles } from "@mui/styles"; import StyledButton from "../../../../../../common/StyledButton"; import PaymentInfoCard from "../../PaymentInfoCard"; -import PurchaseDialog from "../../PurchaseDialog"; -import ChannelSelectionBox from "../../ChannelSelectionBox"; import AlertBox, { alertTypes } from "../../../../../../common/AlertBox"; import { cogsToToken } from "../../../../../../../utility/PricingStrategy"; import { pricing as getPricing } from "../../../../../../../Redux/reducers/ServiceDetailsReducer"; -import PaymentChannelManagement from "../../../../../../../utility/PaymentChannelManagement"; import { loaderActions } from "../../../../../../../Redux/actionCreators"; import { LoaderContent } from "../../../../../../../utility/constants/LoaderContent"; import { useStyles } from "./style"; @@ -17,251 +14,137 @@ import { isUndefined } from "lodash"; import { currentServiceDetails } from "../../../../../../../Redux/reducers/ServiceDetailsReducer"; import { updateMetamaskWallet } from "../../../../../../../Redux/actionCreators/UserActions"; -import { getSdk } from "../../../../../../../Redux/actionCreators/SDKActions"; - -const payTypes = { - CHANNEL_BALANCE: "CHANNEL_BALANCE", - MULTIPLE_CALLS: "MULTIPLE_CALLS", - SINGLE_CALL: "SINGLE_CALL", -}; - -const connectMMinfo = { - type: alertTypes.ERROR, - message: `Please install Metamask and use your Metamask wallet to connect to SingularityNet. -Click below to install and learn more about how to use Metamask and your ${process.env.REACT_APP_TOKEN_NAME} credits with SinguarlityNet AI Marketplace.`, -}; - -const MIN_CALLS_NUMBER = 1; - -const paymentInfoCardDatMpeBal = { - title: "Escrow Balance", - id: "mpeBal", - unit: process.env.REACT_APP_TOKEN_NAME, -}; - -let paymentChannelManagement; +import { createPaymentChannelManagement, getSdk } from "../../../../../../../Redux/actionCreators/SDKActions"; +import { payTypes, connectMMinfo, paymentInfoCardDatMpeBal, insufficientMpeError } from "./metadata"; +import { isCallsMoreOrEqualThanMinimum } from "./helpers"; +import ContinueButton from "./ContinueButton"; +import DepositButton from "./DepositButton"; +import PaymentOptions from "./PaymentOptions"; const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAvailable }) => { const dispatch = useDispatch(); + const paymentChannelManagementRef = useRef(); const { price_in_cogs } = useSelector((state) => getPricing(state)); const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); - const [mpeBalance, setMpeBalance] = useState("0"); + const servicePriceInToken = useMemo(() => cogsToToken(price_in_cogs), [price_in_cogs]); + const [mpeBalance, setMpeBalance] = useState(""); const [selectedPayType, setSelectedPayType] = useState(payTypes.CHANNEL_BALANCE); - const [disabledPayTypes, setDisabledPayTypes] = useState([]); - const [showPurchaseDialog, setShowPurchaseDialog] = useState(false); const [noOfServiceCalls, setNoOfServiceCalls] = useState(1); - const [totalPrice, setTotalPrice] = useState(cogsToToken(price_in_cogs)); + const [totalPrice, setTotalPrice] = useState(servicePriceInToken); const [alert, setAlert] = useState({}); - const [showTooltip, setShowTooltip] = useState(false); const [channelBalance, setChannelBalance] = useState(); const [isStartServiceDisable, setIsStartServiceDisable] = useState(false); - const updateBalanceData = async () => { - try { - await initializedPaymentChannel(); - await getPaymentChannelData(); - setIsStartServiceDisable(false); - } catch (err) { - setIsStartServiceDisable(true); - setAlert({ type: alertTypes.ERROR, message: err.message }); - } - }; - useEffect(() => { - const handleDisabledPaytypes = () => { - const disabledPayTypes = []; - - if (channelBalance <= 0 && !disabledPayTypes.includes(payTypes.CHANNEL_BALANCE)) { - disabledPayTypes.push(payTypes.CHANNEL_BALANCE); - setSelectedPayType(""); - } - if (mpeBalance <= 0) { - if (!disabledPayTypes.includes(payTypes.SINGLE_CALL)) { - disabledPayTypes.push(payTypes.SINGLE_CALL); - } - if (!disabledPayTypes.includes(payTypes.MULTIPLE_CALLS)) { - disabledPayTypes.push(payTypes.MULTIPLE_CALLS); - } - } - setDisabledPayTypes(disabledPayTypes); - }; + dispatch(updateMetamaskWallet()); + }, [dispatch]); - handleDisabledPaytypes(); - }, [channelBalance, mpeBalance]); + useEffect(() => { + if (!paymentChannelManagementRef.current) return; + const totalPriceInCogs = cogsToToken(paymentChannelManagementRef.current.noOfCallsToCogs(noOfServiceCalls)); + setTotalPrice(totalPriceInCogs); + }, [noOfServiceCalls]); - const initializedPaymentChannel = async () => { + const getBalanceData = useCallback(async () => { + setAlert({}); + dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); try { - setAlert({}); - dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); - const sdk = await dispatch(getSdk()); - const serviceClient = await sdk.createServiceClient({ orgId: org_id, serviceId: service_id }); - paymentChannelManagement = new PaymentChannelManagement(sdk, serviceClient); - const escrowBalance = await sdk.account.escrowBalance(); - setMpeBalance(cogsToToken(escrowBalance)); + const channelBalance = paymentChannelManagementRef.current.availableBalance(); + const channelBalanceInCogs = cogsToToken(channelBalance); + + if (channelBalanceInCogs >= totalPrice) { + setIsLastPaidCall(channelBalanceInCogs === totalPrice); + await handleContinueWithChanelBalance(); + return; + } + setChannelBalance(channelBalanceInCogs); + setSelectedPayType(payTypes.MULTIPLE_CALLS); } catch (error) { - console.error("error on initialize Metamask payment channel: ", error); + console.error("get balance error: ", error); setAlert(connectMMinfo); } finally { dispatch(loaderActions.stopAppLoader()); } - }; + }, [dispatch]); - const getPaymentChannelData = async () => { - setAlert({}); + const updateBalanceData = useCallback(async () => { try { + setAlert({}); dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); - await paymentChannelManagement.updateChannelInfo(); + paymentChannelManagementRef.current = await dispatch(createPaymentChannelManagement(org_id, service_id)); + await paymentChannelManagementRef.current.updateChannelInfo(); await getBalanceData(); + const sdk = await dispatch(getSdk()); + const escrowBalance = await sdk.account.escrowBalance(); + setMpeBalance(cogsToToken(escrowBalance)); + setIsStartServiceDisable(false); } catch (error) { + console.error("error on initialize Metamask payment channel: ", error); + setIsStartServiceDisable(true); setAlert(connectMMinfo); } finally { dispatch(loaderActions.stopAppLoader()); } - }; + }, [dispatch, getBalanceData, org_id, service_id]); - const getBalanceData = async () => { - setAlert({}); - dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); + const handleContinueWithChanelBalance = async () => { try { - await dispatch(updateMetamaskWallet()); - const channelBalance = paymentChannelManagement.availableBalance(); - const channelBalanceInCogs = cogsToToken(channelBalance); - setChannelBalance(channelBalanceInCogs); - if (channelBalanceInCogs === totalPrice) { - setIsLastPaidCall(true); - await handleSubmit(); - return; + const isChannelNearToExpiry = await paymentChannelManagementRef.current.isChannelNearToExpiry(); + if (isChannelNearToExpiry) { + await paymentChannelManagementRef.current.extendChannel(); } - if (channelBalanceInCogs > totalPrice) { - setNoOfServiceCalls(channelBalanceInCogs / totalPrice); - setIsLastPaidCall(false); - await handleSubmit(); - return; - } - setSelectedPayType(payTypes.MULTIPLE_CALLS); - } catch (error) { - console.error("get balance error: ", error); - setAlert(connectMMinfo); + handleContinue(); + return; + } catch (e) { + setAlert({ type: alertTypes.ERROR, message: e.message }); } finally { dispatch(loaderActions.stopAppLoader()); } }; - const handlePayTypeChange = (value) => { - if (disabledPayTypes.includes(value) || selectedPayType === value) { - return; - } - setSelectedPayType(value); - }; - - const handlePurchaseDialogOpen = () => { - setShowPurchaseDialog(true); - }; - - const handlePurchaseDialogClose = () => { - setShowPurchaseDialog(false); - }; - - const isValidCallsNumber = (numberOfCalls) => { - const isInteger = numberOfCalls % 1 === 0; - const isNumber = !isNaN(Number(numberOfCalls)); - return isInteger && isNumber; - }; - - const isCallsMoreOrEqualThanMinimum = () => { - return noOfServiceCalls >= MIN_CALLS_NUMBER; - }; - - const formatValue = (value) => { - let stringValue = String(value); - if (stringValue[0] === "0" && stringValue.length > 1) { - return stringValue.slice(1); - } - return value; - }; - - const handleNoOfCallsChange = (event) => { - let noOfServiceCalls = event.target.value; - if (!noOfServiceCalls || noOfServiceCalls === 0) { - noOfServiceCalls = 0; - } - noOfServiceCalls = formatValue(noOfServiceCalls); - setNoOfServiceCalls(noOfServiceCalls); - if (!isValidCallsNumber(noOfServiceCalls)) { - return; - } - const totalPriceInCogs = cogsToToken(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls)); - setTotalPrice(totalPriceInCogs); - }; - - const handleSubmit = async () => { + const handleSubmit = useCallback(async () => { dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); setAlert({}); - if (selectedPayType === payTypes.CHANNEL_BALANCE) { - try { - const isChannelNearToExpiry = await paymentChannelManagement.isChannelNearToExpiry(); - if (isChannelNearToExpiry) { - await paymentChannelManagement.extendChannel(); - } - handleContinue(); - return; - } catch (e) { - setAlert({ type: alertTypes.ERROR, message: e.message }); - } finally { - dispatch(loaderActions.stopAppLoader()); - } - } - - if (selectedPayType === payTypes.SINGLE_CALL) { - setNoOfServiceCalls(1); - } + const paymentChannelManagement = paymentChannelManagementRef.current; if (noOfServiceCalls === 1) { setIsLastPaidCall(true); } try { if (mpeBalance < cogsToToken(paymentChannelManagement.noOfCallsToCogs(noOfServiceCalls))) { - setAlert({ - type: alertTypes.ERROR, - message: `Insufficient MPE balance. Please deposit some ${process.env.REACT_APP_TOKEN_NAME} tokens to your escrow account`, - }); + setAlert(insufficientMpeError); return; } - if (!paymentChannelManagement.channel) { - await paymentChannelManagement.openChannel(noOfServiceCalls); - } else { - await paymentChannelManagement.extendAndAddFunds(noOfServiceCalls); - } + !paymentChannelManagement.channel + ? await paymentChannelManagement.openChannel(noOfServiceCalls) + : await paymentChannelManagement.extendAndAddFunds(noOfServiceCalls); handleContinue(); } catch (error) { + console.error(error); setAlert({ type: alertTypes.ERROR, message: "Unable to execute the call" }); } finally { dispatch(loaderActions.stopAppLoader()); } - }; + }, [dispatch, noOfServiceCalls, setIsLastPaidCall, mpeBalance, handleContinue]); + + const isContinueEnabled = useMemo(() => { + console.log( + isCallsMoreOrEqualThanMinimum(noOfServiceCalls) && + selectedPayType && + isServiceAvailable && + (Number(mpeBalance) >= Number(totalPrice) || Number(channelBalance) >= Number(totalPrice)) + ); - const shouldContinueBeEnabled = () => { return ( - // if pay type multiple calls the call number should be more than MIN_CALLS_NUMBER - (selectedPayType !== payTypes.MULTIPLE_CALLS || isCallsMoreOrEqualThanMinimum()) && + isCallsMoreOrEqualThanMinimum(noOfServiceCalls) && selectedPayType && isServiceAvailable && (Number(mpeBalance) >= Number(totalPrice) || Number(channelBalance) >= Number(totalPrice)) ); - }; - - const shouldDepositToEscrowBeHighlighted = () => mpeBalance <= 0; - - const handleTooltipOpen = () => { - if (!isServiceAvailable) { - setShowTooltip(true); - } - }; + }, [selectedPayType, noOfServiceCalls, isServiceAvailable, mpeBalance, channelBalance, totalPrice]); - const handleTooltipClose = () => { - setShowTooltip(false); - }; + const shouldHighlightDeposit = useMemo(() => mpeBalance <= 0, [mpeBalance]); if (isUndefined(channelBalance) || isNaN(channelBalance)) { return ( @@ -278,68 +161,30 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva
- - handlePayTypeChange(payTypes.SINGLE_CALL)} - inputProps={{ - totalPrice: cogsToToken(price_in_cogs), - unit: process.env.REACT_APP_TOKEN_NAME, - noInput: true, - }} - disabled={disabledPayTypes.includes(payTypes.SINGLE_CALL)} + -
-
Best Value
- handlePayTypeChange(payTypes.MULTIPLE_CALLS)} - inputProps={{ - noOfServiceCalls, - onChange: handleNoOfCallsChange, - totalPrice, - unit: process.env.REACT_APP_TOKEN_NAME, - }} - disabled={disabledPayTypes.includes(payTypes.MULTIPLE_CALLS)} - /> -
- + - -
- -
-
@@ -347,3 +192,10 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva }; export default withStyles(useStyles)(MetamaskFlow); + +MetamaskFlow.propTypes = { + classes: PropTypes.object.isRequired, + handleContinue: PropTypes.func.isRequired, + setIsLastPaidCall: PropTypes.func.isRequired, + isServiceAvailable: PropTypes.bool.isRequired, +}; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/metadata.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/metadata.js new file mode 100644 index 000000000..1dd671968 --- /dev/null +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/metadata.js @@ -0,0 +1,26 @@ +import { alertTypes } from "../../../../../../common/AlertBox"; + +export const payTypes = { + CHANNEL_BALANCE: "CHANNEL_BALANCE", + MULTIPLE_CALLS: "MULTIPLE_CALLS", + SINGLE_CALL: "SINGLE_CALL", +}; + +export const connectMMinfo = { + type: alertTypes.ERROR, + message: `Please install Metamask and use your Metamask wallet to connect to SingularityNet. +Click below to install and learn more about how to use Metamask and your ${process.env.REACT_APP_TOKEN_NAME} credits with SinguarlityNet AI Marketplace.`, +}; + +export const insufficientMpeError = { + type: alertTypes.ERROR, + message: `Insufficient MPE balance. Please deposit some ${process.env.REACT_APP_TOKEN_NAME} tokens to your escrow account`, +}; + +export const MIN_CALLS_NUMBER = 1; + +export const paymentInfoCardDatMpeBal = { + title: "Escrow Balance", + id: "mpeBal", + unit: process.env.REACT_APP_TOKEN_NAME, +}; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/style.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/style.js index df7dc7ca2..1d88d3169 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/style.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/style.js @@ -31,7 +31,7 @@ export const useStyles = (theme) => ({ display: "flex", justifyContent: "space-between", flexWrap: "wrap", - gap: 25, + gap: 24, "@media(max-width:650px)": { justifyContent: "center" }, }, paymentChannelDropDownContainer: { display: "flex" }, @@ -67,9 +67,10 @@ export const useStyles = (theme) => ({ buttonContainer: { textAlign: "center", display: "flex", - justifyContent: "space-evenly", - "& div": { - display: "inline-block", + justifyContent: "center", + gap: 24, + [theme.breakpoints.down("sm")]: { + flexDirection: "column", }, }, tooltip: { fontSize: 14 }, diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/styles.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/styles.js index 4b213262c..dfeb5aa76 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/styles.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/styles.js @@ -1,6 +1,6 @@ export const useStyles = (theme) => ({ mainContainer: { - gap: 30, + gap: 24, display: "flex", flexDirection: "column", "& p": { @@ -40,9 +40,9 @@ export const useStyles = (theme) => ({ paymentChannelAndDetails: { display: "flex", alignItems: "center", - justifyContent: "space-between", flexWrap: "wrap", - gap: 30, + justifyContent: "center", + gap: 24, "@media(max-width:767px)": { flexDirection: "column" }, }, paymentChannelDropDownContainer: { diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/styles.js b/src/components/ServiceDetails/AboutService/ServiceDemo/styles.js index f9e43b0da..88e098ab3 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/styles.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/styles.js @@ -4,6 +4,9 @@ export const useStyles = (theme) => ({ display: "flex", flexDirection: "column", gap: 30, + [theme.breakpoints.down("sm")]: { + padding: 0, + }, }, lastPaidCallInfo: { margin: 0, From 0b96723620c42557b42b10c988789a9a695e4346 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:13:06 +0300 Subject: [PATCH 33/83] [SM-105] update metamask flow callback dependencies --- .../ExpiredSession/MetamaskFlow/index.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index b396bbc02..7d33a100b 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -46,6 +46,21 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva setTotalPrice(totalPriceInCogs); }, [noOfServiceCalls]); + const handleContinueWithChanelBalance = useCallback(async () => { + try { + const isChannelNearToExpiry = await paymentChannelManagementRef.current.isChannelNearToExpiry(); + if (isChannelNearToExpiry) { + await paymentChannelManagementRef.current.extendChannel(); + } + handleContinue(); + return; + } catch (e) { + setAlert({ type: alertTypes.ERROR, message: e.message }); + } finally { + dispatch(loaderActions.stopAppLoader()); + } + }, [dispatch, handleContinue]); + const getBalanceData = useCallback(async () => { setAlert({}); dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); @@ -66,7 +81,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva } finally { dispatch(loaderActions.stopAppLoader()); } - }, [dispatch]); + }, [dispatch, handleContinueWithChanelBalance, setIsLastPaidCall, totalPrice]); const updateBalanceData = useCallback(async () => { try { @@ -88,21 +103,6 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva } }, [dispatch, getBalanceData, org_id, service_id]); - const handleContinueWithChanelBalance = async () => { - try { - const isChannelNearToExpiry = await paymentChannelManagementRef.current.isChannelNearToExpiry(); - if (isChannelNearToExpiry) { - await paymentChannelManagementRef.current.extendChannel(); - } - handleContinue(); - return; - } catch (e) { - setAlert({ type: alertTypes.ERROR, message: e.message }); - } finally { - dispatch(loaderActions.stopAppLoader()); - } - }; - const handleSubmit = useCallback(async () => { dispatch(loaderActions.startAppLoader(LoaderContent.SETUP_CHANNEL_FOR_SERV_EXEC)); setAlert({}); From cb792eaef106e292063726b52382eab41dd16c67 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 15:16:19 +0300 Subject: [PATCH 34/83] [SM-105] remove unecessary logs --- src/Redux/actionCreators/ServiceDetailsActions.js | 2 -- .../Purchase/ExpiredSession/MetamaskFlow/index.js | 7 ------- .../AboutService/ServiceDemo/Purchase/index.js | 1 - 3 files changed, 10 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index b5936eb62..20d3ae7b1 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -150,8 +150,6 @@ export const fetchMeteringData = ({ orgId, serviceId, groupId, freeCallsTotal }) => async (dispatch) => { const freeCallsAvailable = await dispatch(getAvailableFreeCalls(orgId, serviceId, groupId)); - console.log("fetchMeteringData freeCallsAvailable: ", freeCallsAvailable); - dispatch(fetchMeteringDataSuccess(freeCallsAvailable, freeCallsTotal)); return { freeCallsAvailable, freeCallsTotal }; }; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index 7d33a100b..831441d9d 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -129,13 +129,6 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva }, [dispatch, noOfServiceCalls, setIsLastPaidCall, mpeBalance, handleContinue]); const isContinueEnabled = useMemo(() => { - console.log( - isCallsMoreOrEqualThanMinimum(noOfServiceCalls) && - selectedPayType && - isServiceAvailable && - (Number(mpeBalance) >= Number(totalPrice) || Number(channelBalance) >= Number(totalPrice)) - ); - return ( isCallsMoreOrEqualThanMinimum(noOfServiceCalls) && selectedPayType && diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js index a6ba2a83c..95b83eacc 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js @@ -32,7 +32,6 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set freeCallsTotal: free_calls, }) ); - console.log(freeCallsTotal, freeCallsAvailable); setFreeCalls({ freeCallsTotal, From bf5e6fe7a6b61a0f6e2983fac00062a9e4fe2f2f Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 18:00:00 +0300 Subject: [PATCH 35/83] [SM-105] fix payment call --- .../UserProfileAccount/MetamaskDetails/MPEActionTabs.js | 6 ++++-- src/utility/PaymentChannelManagement.js | 2 +- src/utility/PricingStrategy.js | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js index 68e8b8320..137066399 100644 --- a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js +++ b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js @@ -6,7 +6,7 @@ import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; import StyledButton from "../../../common/StyledButton"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; -import { agiToCogs, txnTypes } from "../../../../utility/PricingStrategy"; +import { tokenToCogs, txnTypes } from "../../../../utility/PricingStrategy"; import { sdkActions } from "../../../../Redux/actionCreators"; import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; @@ -58,6 +58,8 @@ const MPEActionTabs = ({ classes }) => { }; const MPEAction = async (txnType, amountInCogs) => { + console.log("amountInCogs", amountInCogs); + const sdk = await dispatch(sdkActions.getSdk()); return await sdk.account[MPEActions[txnType]](amountInCogs); }; @@ -72,7 +74,7 @@ const MPEActionTabs = ({ classes }) => { setAlert({}); try { const amountInAGI = amount[txnType]; - const amountInCogs = agiToCogs(amountInAGI); + const amountInCogs = tokenToCogs(amountInAGI); await MPEAction(txnType, amountInCogs); setAlert({ type: alertTypes.SUCCESS, message: successAlert[txnType] }); } catch (error) { diff --git a/src/utility/PaymentChannelManagement.js b/src/utility/PaymentChannelManagement.js index 54b9aca83..1e18fd0b2 100644 --- a/src/utility/PaymentChannelManagement.js +++ b/src/utility/PaymentChannelManagement.js @@ -90,7 +90,7 @@ export default class PaymentChannelManagement { async isChannelNearToExpiry() { const channelExpiry = (await this._channel?.state?.expiry) ?? 0; const blockNumber = await this._sdkContext.web3.eth.getBlockNumber(); - const threshold = this.serviceClient.group.payment_expiration_threshold; + const threshold = this._metadataProvider.group.payment_expiration_threshold; const difference = Math.abs(Number(channelExpiry - blockNumber)); return difference <= threshold; } diff --git a/src/utility/PricingStrategy.js b/src/utility/PricingStrategy.js index e6cdcf6b5..1d468c982 100644 --- a/src/utility/PricingStrategy.js +++ b/src/utility/PricingStrategy.js @@ -1,4 +1,4 @@ -import { cogsToToken as cogsToTokenSDK } from "snet-sdk-web/utils/tokenUtils"; +import { cogsToToken as cogsToTokenSDK, tokenToCogs as tokenToCogsSDK } from "snet-sdk-web/utils/tokenUtils"; const TOKEN_DIVISIBILITY = { FET: 18, @@ -98,8 +98,7 @@ class MethodPricing { } export const cogsToToken = (cogs) => cogsToTokenSDK(cogs, [process.env.REACT_APP_TOKEN_NAME]); - -export const agiToCogs = (agi) => Math.round(agi * priceData.precision); +export const tokenToCogs = (tokens) => tokenToCogsSDK(tokens, [process.env.REACT_APP_TOKEN_NAME]); export const agiInDecimal = (agi) => parseFloat(agi).toFixed(priceData.divisibility); From 5f6a9b0f8e2b4eda91c87f16043a49aea1f94b4c Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 17 Jun 2025 19:10:10 +0300 Subject: [PATCH 36/83] [SM-105] remove unecessary log --- .../UserProfileAccount/MetamaskDetails/MPEActionTabs.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js index 137066399..7c838af45 100644 --- a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js +++ b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/MPEActionTabs.js @@ -58,8 +58,6 @@ const MPEActionTabs = ({ classes }) => { }; const MPEAction = async (txnType, amountInCogs) => { - console.log("amountInCogs", amountInCogs); - const sdk = await dispatch(sdkActions.getSdk()); return await sdk.account[MPEActions[txnType]](amountInCogs); }; From 921ae4d2eb906fc5a219cc55eddfffce9336f5b4 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 18 Jun 2025 15:21:32 +0300 Subject: [PATCH 37/83] [SM-105] fix payment call --- src/utility/sdk.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utility/sdk.js b/src/utility/sdk.js index 67ff15b8e..03c9da03b 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -1,13 +1,13 @@ import SnetSDK from "snet-sdk-web"; import { FreecallMetadataGenerator, hexStringToBytes } from "snet-sdk-web/utils"; -import MPEContract from "singularitynet-platform-contracts/networks/MultiPartyEscrow"; +import { FreeCallPaymentStrategy, PaidPaymentStrategy } from "snet-sdk-web/paymentStrategies"; +import MPEContract from "singularitynet-platform-contracts/networks/MultiPartyEscrow"; import { APIEndpoints, APIPaths } from "../config/APIEndpoints"; import { initializeAPIOptions, postAPI } from "./API"; import { fetchAuthenticatedUser, walletTypes } from "../Redux/actionCreators/UserActions"; import PaypalPaymentMgmtStrategy from "./PaypalPaymentMgmtStrategy"; import { store } from "../"; -import FreeCallPaymentStrategy from "snet-sdk-web/paymentStrategies/FreeCallPaymentStrategy"; import { getFreeCallSign } from "../Redux/actionCreators/ServiceDetailsActions"; const DEFAULT_GAS_PRICE = 4700000; @@ -215,12 +215,10 @@ export const createServiceClient = async ( callType, wallet ) => { - console.log("createServiceClient"); - const options = generateOptions(callType, wallet, serviceRequestErrorHandler, groupInfo); - console.log("createServiceClient ", options); await createMetadataProvider(org_id, service_id, groupInfo.group_name, options); const serviceClient = await sdk.createServiceClient({ + paymentStrategy: new PaidPaymentStrategy(sdk.account, serviceMetadataProvider), serviceMetadataProvider, options, }); From 791aad5b11d425d2ccfe5c0c94af34a69b62b026 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 18 Jun 2025 15:28:01 +0300 Subject: [PATCH 38/83] [SM-134] move carousel data to frontend --- src/Redux/actionCreators/UiContentActions.js | 16 -------------- src/Redux/actionCreators/index.js | 2 -- src/Redux/reducers/UiContentReducer.js | 21 ------------------- src/Redux/reducers/index.js | 2 -- .../ServiceListingHeader/CarouselMetadata.js | 12 +++++++++++ .../ServiceListingHeader/index.js | 12 ++--------- src/config/APIEndpoints.js | 1 - 7 files changed, 14 insertions(+), 52 deletions(-) delete mode 100644 src/Redux/actionCreators/UiContentActions.js delete mode 100644 src/Redux/reducers/UiContentReducer.js create mode 100644 src/components/AiMarketplace/ServiceListingHeader/CarouselMetadata.js diff --git a/src/Redux/actionCreators/UiContentActions.js b/src/Redux/actionCreators/UiContentActions.js deleted file mode 100644 index df97473f3..000000000 --- a/src/Redux/actionCreators/UiContentActions.js +++ /dev/null @@ -1,16 +0,0 @@ -import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; -import { getAPI } from "../../utility/API"; - -export const UPDATE_CAROUSEL = "UPDATE_CAROUSEL"; - -export const fetchCarousel = () => async (dispatch) => { - const apiName = APIEndpoints.CONTRACT.name; - const path = APIPaths.GET_CAROUSEL; - try { - const fetchCarouselResponse = await getAPI(apiName, path); - - dispatch({ type: UPDATE_CAROUSEL, payload: fetchCarouselResponse.data }); - } catch (error) { - return; - } -}; diff --git a/src/Redux/actionCreators/index.js b/src/Redux/actionCreators/index.js index c59269693..da735ba06 100644 --- a/src/Redux/actionCreators/index.js +++ b/src/Redux/actionCreators/index.js @@ -6,7 +6,6 @@ import * as errorActions from "./ErrorActions"; import * as loaderActions from "./LoaderActions"; import * as stylesActions from "./StylesActions"; import * as paymentActions from "./PaymentActions"; -import * as uiContentActions from "./UiContentActions"; import * as sdkActions from "./SDKActions"; import * as datasetActions from "./DatasetActions"; @@ -20,6 +19,5 @@ export { loaderActions, stylesActions, paymentActions, - uiContentActions, datasetActions, }; diff --git a/src/Redux/reducers/UiContentReducer.js b/src/Redux/reducers/UiContentReducer.js deleted file mode 100644 index 72816ec03..000000000 --- a/src/Redux/reducers/UiContentReducer.js +++ /dev/null @@ -1,21 +0,0 @@ -import { uiContentActions } from "../actionCreators"; - -const InitialUiDetails = { - carousel: [], -}; - -const uiContentReducer = (state = InitialUiDetails, action) => { - switch (action.type) { - case uiContentActions.UPDATE_CAROUSEL: { - return { - ...state, - carousel: action.payload, - }; - } - default: { - return state; - } - } -}; - -export default uiContentReducer; diff --git a/src/Redux/reducers/index.js b/src/Redux/reducers/index.js index 23b2af416..d1b07d8c8 100644 --- a/src/Redux/reducers/index.js +++ b/src/Redux/reducers/index.js @@ -7,7 +7,6 @@ import errorReducer from "./ErrorReducer"; import loaderReducer from "./LoaderReducer"; import stylesReducer from "./StylesReducer"; import paymentReducer from "./PaymentReducer"; -import uiContentReducer from "./UiContentReducer"; import sdkReducer from "./SDKReducer"; import datasetReducer from "./DatasetReducer"; @@ -20,7 +19,6 @@ const rootReducer = combineReducers({ loaderReducer, stylesReducer, paymentReducer, - uiContentReducer, sdkReducer, datasetReducer, }); diff --git a/src/components/AiMarketplace/ServiceListingHeader/CarouselMetadata.js b/src/components/AiMarketplace/ServiceListingHeader/CarouselMetadata.js new file mode 100644 index 000000000..18531ef2e --- /dev/null +++ b/src/components/AiMarketplace/ServiceListingHeader/CarouselMetadata.js @@ -0,0 +1,12 @@ +export const carousel = [ + { + id: 1, + image: "https://d16o4vcu292x9v.cloudfront.net/dapp/assets/images/Banners/1.png", + alt_text: "song splitter", + image_alignment: "LEFT", + title: "Song/Splitter is the first mobile app to leverage SingularityNET AI services.", + description: + "Song/Splitter is an AI-driven application for splitting music and vocals into separate tracks, saving them as separate audio files. The app leverages the AI service of Deezer Spleeter which is available for demo and integration. ", + cta: [], + }, +]; diff --git a/src/components/AiMarketplace/ServiceListingHeader/index.js b/src/components/AiMarketplace/ServiceListingHeader/index.js index fda8df6fe..c66bcc40d 100644 --- a/src/components/AiMarketplace/ServiceListingHeader/index.js +++ b/src/components/AiMarketplace/ServiceListingHeader/index.js @@ -1,5 +1,4 @@ -import React, { Fragment, useEffect } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import React, { Fragment } from "react"; import Slider from "react-slick"; import "slick-carousel/slick/slick.css"; import "slick-carousel/slick/slick-theme.css"; @@ -10,16 +9,9 @@ import StarRateIcon from "@mui/icons-material/StarRate"; import { useStyles } from "./styles"; import StyledButton from "../../common/StyledButton"; -import { uiContentActions } from "../../../Redux/actionCreators"; +import { carousel } from "./CarouselMetadata"; const ServiceListingHeader = ({ classes }) => { - const dispatch = useDispatch(); - const carousel = useSelector((state) => state.uiContentReducer.carousel); - - useEffect(() => { - dispatch(uiContentActions.fetchCarousel()); - }, [dispatch]); - const isMoreThanOneElement = () => { return carousel.length > 1; }; diff --git a/src/config/APIEndpoints.js b/src/config/APIEndpoints.js index d3fc75e77..5199e3bfc 100644 --- a/src/config/APIEndpoints.js +++ b/src/config/APIEndpoints.js @@ -40,7 +40,6 @@ export const APIPaths = { GET_SERVICE_LIST: "/service", FILTER_DATA: "/service?attribute=", SERVICE_DETAILS: (orgId, serviceId) => `/org/${orgId}/service/${serviceId}`, - GET_CAROUSEL: "/uicontent/marketplacecarousel", UPDATE_CHANNEL_BALANCE: (channelId) => `/channel/${channelId}/balance`, LINKED_PROVIDERS: "/v2/channel", SIGNER_FREE_CALL: "/freecall", From ea42d48a3852a64bfd77a46bfd490478affcd9c0 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 18 Jun 2025 15:29:39 +0300 Subject: [PATCH 39/83] [SM-43] fix get the service data from redux --- src/Redux/actionCreators/ServiceActions.js | 13 ++----------- .../actionCreators/ServiceDetailsActions.js | 6 +----- src/Redux/reducers/ServiceDetailsReducer.js | 19 +------------------ .../Purchase/ActiveSession/index.js | 11 +++++++---- .../PaymentPopup/Summary/index.js | 7 +++---- .../ExpiredSession/MetamaskFlow/index.js | 9 ++++++--- .../ServiceDetails/ExistingModel/index.js | 3 +-- .../CreateModel/ModelInfo/index.js | 3 +-- src/components/ServiceDetails/index.js | 10 ++-------- 9 files changed, 24 insertions(+), 57 deletions(-) diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index 58e8e2adb..42de8ed09 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -24,16 +24,7 @@ export const resetFilterItem = (dispatch) => { }; export const fetchServiceSuccess = (res) => (dispatch) => { - dispatch({ - type: UPDATE_PAGINATION_DETAILS, - payload: { - total_count: res.data.total_count, - }, - }); - // const enhancedResult = res.data.result.map(service => ({ - // ...service, - // media: { ...service.media, url: service.media.url ? cacheS3Url(service.media.url) : null }, - // })); + dispatch(updatePagination({ total_count: res.data.total_count })); dispatch({ type: UPDATE_SERVICE_LIST, payload: res.data.result }); dispatch(loaderActions.stopAIServiceListLoader()); }; @@ -59,8 +50,8 @@ const onlyUserOrgsFilter = () => async (dispatch) => { export const fetchService = (pagination, filters = []) => async (dispatch) => { + // env variable is string if (process.env.REACT_APP_IS_ALL_SERVICES_AVAILIBLE !== "true") { - // env variable is string filters = await dispatch(onlyUserOrgsFilter()); } dispatch(loaderActions.startAIServiceListLoader()); diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 20d3ae7b1..8d3289170 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -27,16 +27,12 @@ const fetchServiceDetailsFailure = (err) => (dispatch) => { }; const fetchServiceDetailsSuccess = (serviceDetails) => (dispatch) => { - // const enhancedServiceDetails = { - // ...serviceDetails, - // data: { ...serviceDetails.data, media: serviceDetails.data.media.map(el => ({ ...el, url: cacheS3Url(el.url) })) }, - // }; dispatch(loaderActions.stopAppLoader()); dispatch({ type: UPDATE_SERVICE_DETAILS, payload: serviceDetails.data }); }; const fetchServiceDetailsAPI = async (orgId, serviceId) => { - const url = `${APIEndpoints.CONTRACT.endpoint}/org/${orgId}/service/${serviceId}`; + const url = APIEndpoints.CONTRACT.endpoint + APIPaths.SERVICE_DETAILS(orgId, serviceId); const response = await fetch(url); return response.json(); }; diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index 129805f85..9a8fad72e 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -37,27 +37,10 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { } }; -export const currentServiceDetails = (state) => { - return state.serviceDetailsReducer.details; -}; - -export const serviceDetails = (state, orgId, serviceId) => { - const { org_id, service_id } = currentServiceDetails(state); - if (org_id !== orgId || service_id !== serviceId) { - return undefined; - } - - return currentServiceDetails(state); -}; - -const groups = (state) => { - return state.serviceDetailsReducer.details.groups; -}; - const enhanceGroup = (group) => ({ ...group, endpoints: map(group.endpoints, ({ endpoint }) => endpoint) }); export const groupInfo = (state) => { - const serviceGroups = groups(state); + const serviceGroups = state.serviceDetailsReducer.details.groups; const availableGroup = find(serviceGroups, ({ endpoints }) => some(endpoints, (endpoint) => endpoint.is_available === 1) ); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js index d2830b466..c1ffba297 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js @@ -9,14 +9,13 @@ import { useStyles } from "./styles"; import { getIsTrainingAvailable } from "../../../../../../Redux/actionCreators/ServiceDetailsActions"; import { useDispatch, useSelector } from "react-redux"; import { getTrainingModels } from "../../../../../../Redux/actionCreators/ServiceTrainingActions"; -import { currentServiceDetails } from "../../../../../../Redux/reducers/ServiceDetailsReducer"; import { isUndefined } from "lodash"; import { updateMetamaskWallet } from "../../../../../../Redux/actionCreators/UserActions"; const ActiveSession = ({ classes, freeCallsAvailable, handleComplete, freeCallsTotal, isServiceAvailable }) => { const dispatch = useDispatch(); const { detailsTraining } = useSelector((state) => state.serviceDetailsReducer); - const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); + const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); const { modelsList } = useSelector((state) => state.serviceTrainingReducer); const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const [showTooltip, setShowTooltip] = useState(false); @@ -36,8 +35,12 @@ const ActiveSession = ({ classes, freeCallsAvailable, handleComplete, freeCallsT const isTrainingAvailable = getIsTrainingAvailable(detailsTraining, isLoggedIn); const handleRequestModels = async () => { - const address = await dispatch(updateMetamaskWallet()); - await dispatch(getTrainingModels(org_id, service_id, address)); + try { + const address = await dispatch(updateMetamaskWallet()); + await dispatch(getTrainingModels(org_id, service_id, address)); + } catch (error) { + console.error("handle request model: ", error); + } }; const isActionsDisabled = !isServiceAvailable; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Summary/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Summary/index.js index fd7dcc874..acda94532 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Summary/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Summary/index.js @@ -9,7 +9,6 @@ import { useStyles } from "./styles"; import StyledTable from "../../../../../../../../common/StyledTable"; import InfoIcon from "@mui/icons-material/Info"; import { agiInDecimal } from "../../../../../../../../../utility/PricingStrategy"; -import { currentServiceDetails } from "../../../../../../../../../Redux/reducers/ServiceDetailsReducer"; import { orderTypes } from "../../../../../../../../../utility/constants/PaymentConstants"; const successMessage = { @@ -19,7 +18,7 @@ const successMessage = { }; const Summary = (props) => { - const { classes, amount, item, quantity, handlePaymentComplete, serviceDetails, orderType } = props; + const { classes, amount, item, quantity, handlePaymentComplete, organizationName, orderType } = props; const columns = [ { key: "item", label: "Total $USD spent" }, @@ -40,7 +39,7 @@ const Summary = (props) => { return (
- {successMessage[orderType]} {serviceDetails.organization_name} + {successMessage[orderType]} {organizationName}
@@ -51,7 +50,7 @@ const Summary = (props) => { }; const mapStateToProps = (state) => ({ - serviceDetails: currentServiceDetails(state), + organizationName: state.serviceDetailsReducer.details.organization_name, }); export default connect(mapStateToProps)(withStyles(useStyles)(Summary)); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index 831441d9d..728904f45 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -12,7 +12,6 @@ import { LoaderContent } from "../../../../../../../utility/constants/LoaderCont import { useStyles } from "./style"; import { isUndefined } from "lodash"; -import { currentServiceDetails } from "../../../../../../../Redux/reducers/ServiceDetailsReducer"; import { updateMetamaskWallet } from "../../../../../../../Redux/actionCreators/UserActions"; import { createPaymentChannelManagement, getSdk } from "../../../../../../../Redux/actionCreators/SDKActions"; import { payTypes, connectMMinfo, paymentInfoCardDatMpeBal, insufficientMpeError } from "./metadata"; @@ -25,7 +24,7 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva const dispatch = useDispatch(); const paymentChannelManagementRef = useRef(); const { price_in_cogs } = useSelector((state) => getPricing(state)); - const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); + const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); const servicePriceInToken = useMemo(() => cogsToToken(price_in_cogs), [price_in_cogs]); const [mpeBalance, setMpeBalance] = useState(""); @@ -37,7 +36,11 @@ const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAva const [isStartServiceDisable, setIsStartServiceDisable] = useState(false); useEffect(() => { - dispatch(updateMetamaskWallet()); + try { + dispatch(updateMetamaskWallet()); + } catch (err) { + console.error(err); + } }, [dispatch]); useEffect(() => { diff --git a/src/components/ServiceDetails/ExistingModel/index.js b/src/components/ServiceDetails/ExistingModel/index.js index 497b136f4..3dc41f80f 100644 --- a/src/components/ServiceDetails/ExistingModel/index.js +++ b/src/components/ServiceDetails/ExistingModel/index.js @@ -5,7 +5,6 @@ import { useStyles } from "./styles"; import ModelDetails from "./ModelDetails"; import { loaderActions, userActions } from "../../../Redux/actionCreators"; import { LoaderContent } from "../../../utility/constants/LoaderContent"; -import { currentServiceDetails } from "../../../Redux/reducers/ServiceDetailsReducer"; import AlertBox, { alertTypes } from "../../common/AlertBox"; import Card from "../../common/Card"; import { getTrainingModels } from "../../../Redux/actionCreators/ServiceTrainingActions"; @@ -13,7 +12,7 @@ import { isUndefined } from "lodash"; import StyledButton from "../../common/StyledButton"; const ExistingModel = ({ classes, openEditModel }) => { - const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); + const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); const { modelsList } = useSelector((state) => state.serviceTrainingReducer); const { address } = useSelector((state) => state.userReducer.wallet); diff --git a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js index 534c3db96..092f81c9d 100644 --- a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js +++ b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js @@ -9,7 +9,6 @@ import StyledButton from "../../../../common/StyledButton"; import { loaderActions } from "../../../../../Redux/actionCreators"; import { createModel, deleteModel } from "../../../../../Redux/actionCreators/ServiceTrainingActions"; import { LoaderContent } from "../../../../../utility/constants/LoaderContent"; -import { currentServiceDetails } from "../../../../../Redux/reducers/ServiceDetailsReducer"; import AlertBox, { alertTypes } from "../../../../common/AlertBox"; import { withStyles } from "@mui/styles"; @@ -21,7 +20,7 @@ const ModelInfo = ({ classes, cancelEditModel }) => { const dispatch = useDispatch(); const { detailsTraining } = useSelector((state) => state.serviceDetailsReducer); const { currentModel } = useSelector((state) => state.serviceTrainingReducer); - const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); + const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); // const [trainingMethod, setTrainingMethod] = useState(currentModel ? currentModel.methodName : undefined); //eslint-disable-next-line diff --git a/src/components/ServiceDetails/index.js b/src/components/ServiceDetails/index.js index 79f37105d..383855a55 100644 --- a/src/components/ServiceDetails/index.js +++ b/src/components/ServiceDetails/index.js @@ -21,10 +21,6 @@ import { fetchServiceDetails, getIsTrainingAvailable, } from "../../Redux/actionCreators/ServiceDetailsActions"; -import { - serviceDetails as getServiceDetails, - groupInfo as getGroupInfo, -} from "../../Redux/reducers/ServiceDetailsReducer"; import ErrorBox from "../common/ErrorBox"; import SeoMetadata from "../common/SeoMetadata"; @@ -49,8 +45,7 @@ const ServiceDetails = ({ classes }) => { const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const detailsTraining = useSelector((state) => state.serviceDetailsReducer.detailsTraining); - const service = useSelector((state) => getServiceDetails(state, orgId, serviceId)); - const groupInfo = useSelector((state) => getGroupInfo(state)); + const service = useSelector((state) => state.serviceDetailsReducer.details); const loading = useSelector((state) => state.loaderReducer.app.loading); const [activeTab, setActiveTab] = useState(tabId ? tabId : 0); @@ -82,7 +77,6 @@ const ServiceDetails = ({ classes }) => { }; if (isEmpty(service)) { - // || !isEmpty(alert)) { if (loading) { return null; } @@ -119,7 +113,7 @@ const ServiceDetails = ({ classes }) => { name: "Install and Run", tabId: "serviceGuides", activeIndex: 1, - component: , + component: , //TODO remove service attribute }, ]; From e1cf894047f35fe24f565c134763b643e9a71df5 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 18 Jun 2025 15:32:29 +0300 Subject: [PATCH 40/83] [SM-105] remove getting freecall token form --- src/Redux/actionCreators/ServiceActions.js | 37 ----- .../FreecallToken/index.js | 128 ------------------ .../InstallAndRunService/index.js | 7 +- src/utility/constants/LoaderContent.js | 4 - 4 files changed, 1 insertion(+), 175 deletions(-) delete mode 100644 src/components/ServiceDetails/InstallAndRunService/FreecallToken/index.js diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index 42de8ed09..bb56945ac 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -1,6 +1,5 @@ import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; import { loaderActions, userActions } from "./"; -import { LoaderContent } from "../../utility/constants/LoaderContent"; import { getAPI, postAPI, initializeAPIOptions } from "../../utility/API"; import { generateOrganizationsFilterObject } from "../../utility/constants/Pagination"; // import { cacheS3Url } from "../../utility/image"; @@ -110,42 +109,6 @@ const fetchFeedbackAPI = (orgId, serviceId, token) => { return getAPI(apiName, path, apiOptions); }; -const fetchAuthTokenAPI = async (serviceId, groupId, publicKey, orgId, token) => { - const apiName = APIEndpoints.SIGNER_SERVICE.name; - const apiPath = APIPaths.FREE_CALL_TOKEN; - const queryParams = { - service_id: serviceId, - group_id: groupId, - public_key: publicKey, - org_id: orgId, - }; - const apiOptions = initializeAPIOptions(token, null, queryParams); - const authTokenRequest = await getAPI(apiName, apiPath, apiOptions); - return authTokenRequest; -}; - -export const downloadAuthToken = (serviceId, groupId, publicKey, orgId) => async (dispatch) => { - try { - dispatch(loaderActions.startAppLoader(LoaderContent.GENERATE_AUTH_TOKEN)); - const { token, email } = await dispatch(userActions.fetchAuthenticatedUser()); - - const { data } = await fetchAuthTokenAPI(serviceId, groupId, publicKey, orgId, token); - - const jsonToDownload = { - email, - tokenToMakeFreeCall: data.token_to_make_free_call, - tokenExpirationBlock: data.token_expiration_block, - }; - const downloadBlob = new Blob([JSON.stringify(jsonToDownload)], { type: "text/json;charset=utf-8" }); - const downloadURL = window.URL.createObjectURL(downloadBlob); - dispatch(loaderActions.stopAppLoader()); - return downloadURL; - } catch (e) { - dispatch(loaderActions.stopAppLoader()); - throw e; - } -}; - //Username review export const fetchFeedback = (orgId, serviceId) => async (dispatch) => { const { token } = await dispatch(userActions.fetchAuthenticatedUser()); diff --git a/src/components/ServiceDetails/InstallAndRunService/FreecallToken/index.js b/src/components/ServiceDetails/InstallAndRunService/FreecallToken/index.js deleted file mode 100644 index 954aa221e..000000000 --- a/src/components/ServiceDetails/InstallAndRunService/FreecallToken/index.js +++ /dev/null @@ -1,128 +0,0 @@ -import { useState } from "react"; -import Web3 from "web3"; -import { useDispatch } from "react-redux"; -import { useStyles } from "../styles"; -import { withStyles } from "@mui/styles"; -import { downloadAuthToken } from "../../../../Redux/actionCreators/ServiceActions"; -import AlertBox, { alertTypes } from "../../../common/AlertBox"; -import UnauthenticatedDummyToggler from "../../../common/UnauthenticatedDummyToggler"; -import TextField from "@mui/material/TextField"; -import Typography from "@mui/material/Typography"; -import StyledButton from "../../../common/StyledButton"; -import clsx from "clsx"; - -const web3 = new Web3(process.env.REACT_APP_WEB3_PROVIDER, null, {}); -const downloadTokenFileName = "authToken.txt"; - -const parseError = (e) => { - let errorString; - try { - console.error(e, e.response); - const responseBody = JSON.parse(JSON.parse(e.response.body)); - errorString = responseBody.error.split("'")[1]; - } catch (e) { - console.error(e); - errorString = "unknown error"; - } - return "Unable to download the token: " + errorString; -}; - -const FreecallToken = ({ classes, service, groupId }) => { - const dispatch = useDispatch(); - - const [isTokenGenerating, setIsTokenGenerating] = useState(false); - const [isAddressValid, setIsAddressValid] = useState(true); - const [downloadTokenURL, setDownloadTokenURL] = useState(""); - const [publickey, setPublickey] = useState(""); - const [alert, setAlert] = useState({}); - - const generateToken = async () => { - if (isTokenGenerating) { - return; - } - - try { - setIsTokenGenerating(true); - setAlert({}); - const downloadToken = await dispatch(downloadAuthToken(service.service_id, groupId, publickey, service.org_id)); - setDownloadTokenURL(downloadToken); - } catch (e) { - const errorString = parseError(e); - setAlert({ type: alertTypes.ERROR, message: errorString }); - } finally { - setIsTokenGenerating(false); - } - }; - - const isValidAddress = (address) => { - try { - const checksumAddress = web3.utils.toChecksumAddress(address); - return checksumAddress ? true : false; - } catch (error) { - return false; - } - }; - - const handlePublicKey = (currentvalue) => { - console.log(currentvalue); - const address = currentvalue; - setPublickey(address); - const isAddressValid = isValidAddress(address); - if (!isAddressValid) { - setAlert({ type: alertTypes.ERROR, message: "invalid public key" }); - setIsAddressValid(false); - } else { - setAlert({}); - setIsAddressValid(true); - } - }; - - return ( -
-
- - Generate the free call token to use in your SDK. The address used to generate this token should be the same as - the identity specified in your SDK configuation. This will allow you to invoke the service from your SDK on a - trial basis - - - -
-
- handlePublicKey(event.target.value)} - /> - - Ethereum address used in your SDK. This is the public address corresponding to the private key you use - in the SDK - -
- {!downloadTokenURL && ( - - )} - {downloadTokenURL && ( - - - - )} -
-
- - -
-
- ); -}; - -export default withStyles(useStyles)(FreecallToken); diff --git a/src/components/ServiceDetails/InstallAndRunService/index.js b/src/components/ServiceDetails/InstallAndRunService/index.js index c02d8e2e0..e5c0dede5 100644 --- a/src/components/ServiceDetails/InstallAndRunService/index.js +++ b/src/components/ServiceDetails/InstallAndRunService/index.js @@ -6,11 +6,10 @@ import StyledTabs from "../StyledTabs"; import ProjectDetails from "../ProjectDetails"; import { useStyles } from "./styles"; import Card from "../../common/Card"; -import FreecallToken from "./FreecallToken"; import { tabNames } from "./TabsMeta"; import IntegrationFilesActions from "./IntegrationFilesActions"; -const InstallAndRunService = ({ classes, service, groupId }) => { +const InstallAndRunService = ({ classes, service }) => { const [activeTab, setActiveTab] = useState(0); const handleTabChange = (newActiveTab) => { @@ -37,10 +36,6 @@ const InstallAndRunService = ({ classes, service, groupId }) => { header="Integration Setup" children={} /> - } - /> Date: Wed, 18 Jun 2025 15:33:10 +0300 Subject: [PATCH 41/83] [SM-43] refactor list of service component --- .../MainSection/ServiceCollection/index.js | 54 +++++++++---------- .../AiMarketplace/MainSection/index.js | 5 +- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/components/AiMarketplace/MainSection/ServiceCollection/index.js b/src/components/AiMarketplace/MainSection/ServiceCollection/index.js index 4770eed98..ec7a6cb3a 100644 --- a/src/components/AiMarketplace/MainSection/ServiceCollection/index.js +++ b/src/components/AiMarketplace/MainSection/ServiceCollection/index.js @@ -1,6 +1,5 @@ import React from "react"; import { Link } from "react-router-dom"; -import { connect } from "react-redux"; import CircularProgress from "@mui/material/CircularProgress"; import truncate from "lodash/truncate"; @@ -8,12 +7,18 @@ import ServiceListItem from "./ServiceListItem"; import { useStyles } from "./styles"; import Routes from "../../../../utility/constants/Routes"; import GridViewItem from "./GridViewItem"; +import { useSelector } from "react-redux"; const maxDescriptionChars = 180; -const CardGroup = ({ data: cards = [], listView, loading }) => { +const CardGroup = ({ listView }) => { const classes = useStyles(); + const services = useSelector((state) => state.serviceReducer.services); + const loading = useSelector((state) => state.loaderReducer.aiServieList); + + const cards = services ? services : []; + if (loading) { return (
@@ -24,6 +29,7 @@ const CardGroup = ({ data: cards = [], listView, loading }) => {
); } + if (cards.length === 0) { return (
@@ -33,38 +39,30 @@ const CardGroup = ({ data: cards = [], listView, loading }) => { ); } + const cardPropsGenerate = (card) => { + return { + cardMedia: card.media.url, + orgImg: card.org_assets_url.hero_image, + cardTitle: card.display_name, + cardSubheader: card.organization_name, + ratingGiven: card.service_rating, + cardDescription: truncate(card.short_description, { length: maxDescriptionChars }), + isAvailable: Boolean(card.is_available), + }; + }; + return (
{cards.map((card) => { + const cardProps = cardPropsGenerate(card); + return ( - {listView ? ( - - ) : ( - - )} + {listView ? : } ); })} @@ -72,8 +70,4 @@ const CardGroup = ({ data: cards = [], listView, loading }) => { ); }; -const mapStateToProps = (state) => ({ - loading: state.loaderReducer.aiServieList, -}); - -export default connect(mapStateToProps)(CardGroup); +export default CardGroup; diff --git a/src/components/AiMarketplace/MainSection/index.js b/src/components/AiMarketplace/MainSection/index.js index d52f2135d..ddf9e6933 100644 --- a/src/components/AiMarketplace/MainSection/index.js +++ b/src/components/AiMarketplace/MainSection/index.js @@ -44,7 +44,7 @@ class MainSection extends Component { }; render() { - const { classes, services, pagination } = this.props; + const { classes, pagination } = this.props; const { listView } = this.state; return ( @@ -59,7 +59,7 @@ class MainSection extends Component { /> - + ({ - services: state.serviceReducer.services, pagination: state.serviceReducer.pagination, currentFilter: state.serviceReducer.activeFilterItem, }); From a12c3d71d866286ac94816e1a16b7a051202d05c Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Thu, 19 Jun 2025 13:10:10 +0300 Subject: [PATCH 42/83] [SM-43] fix switch between services --- .../actionCreators/ServiceDetailsActions.js | 4 ++-- src/Redux/reducers/ServiceDetailsReducer.js | 13 +++++++++++++ .../AboutService/ServiceDemo/Purchase/index.js | 18 +++++++++++------- src/components/ServiceDetails/index.js | 3 ++- src/utility/sdk.js | 6 +++++- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 8d3289170..ab48a13e2 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -28,7 +28,7 @@ const fetchServiceDetailsFailure = (err) => (dispatch) => { const fetchServiceDetailsSuccess = (serviceDetails) => (dispatch) => { dispatch(loaderActions.stopAppLoader()); - dispatch({ type: UPDATE_SERVICE_DETAILS, payload: serviceDetails.data }); + dispatch({ type: UPDATE_SERVICE_DETAILS, payload: serviceDetails }); }; const fetchServiceDetailsAPI = async (orgId, serviceId) => { @@ -43,7 +43,7 @@ export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { dispatch(resetServiceDetails); dispatch(resetCurrentModelDetails()); dispatch(resetModelList()); - const serviceDetails = await fetchServiceDetailsAPI(orgId, serviceId); + const { data: serviceDetails } = await fetchServiceDetailsAPI(orgId, serviceId); dispatch(fetchServiceDetailsSuccess(serviceDetails)); } catch (error) { dispatch(fetchServiceDetailsFailure(error)); diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index 9a8fad72e..677037562 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -39,6 +39,19 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { const enhanceGroup = (group) => ({ ...group, endpoints: map(group.endpoints, ({ endpoint }) => endpoint) }); +export const currentServiceDetails = (state) => { + return state.serviceDetailsReducer.details; +}; + +export const serviceDetails = (state, orgId, serviceId) => { + const { org_id, service_id } = currentServiceDetails(state); + if (org_id !== orgId || service_id !== serviceId) { + return undefined; + } + + return currentServiceDetails(state); +}; + export const groupInfo = (state) => { const serviceGroups = state.serviceDetailsReducer.details.groups; const availableGroup = find(serviceGroups, ({ endpoints }) => diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js index 95b83eacc..1da894305 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js @@ -2,18 +2,22 @@ import React, { useEffect, useState } from "react"; import ActiveSession from "./ActiveSession"; import ExpiredSession from "./ExpiredSession"; -import { groupInfo } from "../../../../../Redux/reducers/ServiceDetailsReducer"; import { useDispatch, useSelector } from "react-redux"; import { loaderActions, serviceDetailsActions } from "../../../../../Redux/actionCreators"; import { LoaderContent } from "../../../../../utility/constants/LoaderContent"; import CircularProgress from "@material-ui/core/CircularProgress"; import "./styles.css"; -import { isUndefined } from "lodash"; +import { isUndefined, some } from "lodash"; +import { useParams } from "react-router-dom"; const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, setIsLastPaidCall }) => { const dispatch = useDispatch(); - const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); - const { free_calls, group_id } = useSelector((state) => groupInfo(state)); + const { orgId, serviceId } = useParams(); + const { free_calls, group_id } = useSelector((state) => + state.serviceDetailsReducer.details.groups.find(({ endpoints }) => + some(endpoints, (endpoint) => endpoint.is_available === 1) + ) + ); const email = useSelector((state) => state.userReducer.email); const [isFreecallLoading, setIsFreecallLoading] = useState(false); @@ -26,8 +30,8 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set setIsFreecallLoading(true); const { freeCallsAvailable, freeCallsTotal } = await dispatch( serviceDetailsActions.fetchMeteringData({ - orgId: org_id, - serviceId: service_id, + orgId, + serviceId, groupId: group_id, freeCallsTotal: free_calls, }) @@ -50,7 +54,7 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set }; fetchFreeCallsUsage(); - }, [dispatch, org_id, service_id, group_id, email, free_calls]); + }, [dispatch, orgId, serviceId, group_id, email, free_calls]); if (isFreecallLoading || isUndefined(freeCalls.freeCallsAvailable)) { return ( diff --git a/src/components/ServiceDetails/index.js b/src/components/ServiceDetails/index.js index 383855a55..4ff2c82f3 100644 --- a/src/components/ServiceDetails/index.js +++ b/src/components/ServiceDetails/index.js @@ -21,6 +21,7 @@ import { fetchServiceDetails, getIsTrainingAvailable, } from "../../Redux/actionCreators/ServiceDetailsActions"; +import { serviceDetails as getServiceDetails } from "../../Redux/reducers/ServiceDetailsReducer"; import ErrorBox from "../common/ErrorBox"; import SeoMetadata from "../common/SeoMetadata"; @@ -45,7 +46,7 @@ const ServiceDetails = ({ classes }) => { const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const detailsTraining = useSelector((state) => state.serviceDetailsReducer.detailsTraining); - const service = useSelector((state) => state.serviceDetailsReducer.details); + const service = useSelector((state) => getServiceDetails(state, orgId, serviceId)); const loading = useSelector((state) => state.loaderReducer.app.loading); const [activeTab, setActiveTab] = useState(tabId ? tabId : 0); diff --git a/src/utility/sdk.js b/src/utility/sdk.js index 03c9da03b..155a4fd47 100644 --- a/src/utility/sdk.js +++ b/src/utility/sdk.js @@ -56,7 +56,11 @@ const parseFreeCallMetadata = (data) => { }; export const createFreecallStrategy = async (org_id, service_id, group_name, options) => { - if (!serviceMetadataProvider) { + if ( + !serviceMetadataProvider || + serviceMetadataProvider.serviceMetadata.orgId !== org_id || + serviceMetadataProvider.serviceMetadata.serviceId !== service_id + ) { await createMetadataProvider(org_id, service_id, group_name, options); } From b8b6344ce2fd30bb02f5e95e5c0c246615689446 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Fri, 20 Jun 2025 16:10:45 +0300 Subject: [PATCH 43/83] [SM-43] update system of parsing service details --- .../actionCreators/ServiceTrainingActions.js | 9 ----- src/Redux/reducers/ServiceDetailsReducer.js | 40 ------------------- .../AboutService/DemoToggler.js | 3 +- .../PaymentPopup/Details/index.js | 7 +--- .../GeneralAccountWallet/index.js | 3 +- .../MetamaskFlow/ContinueButton.jsx | 1 - .../ExpiredSession/MetamaskFlow/index.js | 6 +-- .../ServiceDemo/ThirdPartyAIService.js | 3 +- .../CreatorDetails/Contacts/index.js | 22 ++-------- .../ServiceDetails/CreatorDetails/index.js | 11 +---- .../ServiceDetails/PricingDetails/index.js | 3 +- src/components/ServiceDetails/index.js | 24 +++++------ .../MetamaskDetails/styles.js | 1 - 13 files changed, 24 insertions(+), 109 deletions(-) diff --git a/src/Redux/actionCreators/ServiceTrainingActions.js b/src/Redux/actionCreators/ServiceTrainingActions.js index 12b069a80..204fba05c 100644 --- a/src/Redux/actionCreators/ServiceTrainingActions.js +++ b/src/Redux/actionCreators/ServiceTrainingActions.js @@ -105,15 +105,6 @@ export const deleteModel = (organizationId, serviceId, modelId, methodName, serv } }; -// export const getServiceName = () => (getState) => { -// // const { serviceDetailsReducer, serviceTrainingReducer } = getState(); -// // if (serviceTrainingReducer.serviceName) { -// // return serviceTrainingReducer.serviceName; -// // } -// const training = serviceDetailsReducer.training; -// return getServiceNameFromTrainingMethod(training.trainingMethods[0]); -// }; - const getServiceNameFromTrainingMethod = (trainingMethod) => { return trainingMethod.split(".")[1].split("/")[0]; }; diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index 677037562..c1575ef3a 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -1,8 +1,4 @@ import { serviceDetailsActions } from "../actionCreators"; -import find from "lodash/find"; -import first from "lodash/first"; -import some from "lodash/some"; -import map from "lodash/map"; const InitialServiceDetails = { freeCalls: { @@ -37,40 +33,4 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { } }; -const enhanceGroup = (group) => ({ ...group, endpoints: map(group.endpoints, ({ endpoint }) => endpoint) }); - -export const currentServiceDetails = (state) => { - return state.serviceDetailsReducer.details; -}; - -export const serviceDetails = (state, orgId, serviceId) => { - const { org_id, service_id } = currentServiceDetails(state); - if (org_id !== orgId || service_id !== serviceId) { - return undefined; - } - - return currentServiceDetails(state); -}; - -export const groupInfo = (state) => { - const serviceGroups = state.serviceDetailsReducer.details.groups; - const availableGroup = find(serviceGroups, ({ endpoints }) => - some(endpoints, (endpoint) => endpoint.is_available === 1) - ); - if (availableGroup) { - return enhanceGroup(availableGroup); - } - const firstGroup = first(serviceGroups); - if (firstGroup) { - return enhanceGroup(firstGroup); - } -}; - -export const pricing = (state) => { - const group = groupInfo(state); - if (!group) return {}; - - return find(group.pricing, (price) => price.default === true); -}; - export default serviceDetailsReducer; diff --git a/src/components/ServiceDetails/AboutService/DemoToggler.js b/src/components/ServiceDetails/AboutService/DemoToggler.js index 57093cf18..008fc612e 100644 --- a/src/components/ServiceDetails/AboutService/DemoToggler.js +++ b/src/components/ServiceDetails/AboutService/DemoToggler.js @@ -19,12 +19,11 @@ const DemoToggler = ({ demoComponentRequired, }) => { const [isModalVisible, setIsModalVisible] = useState(false); - const providerSupport = service.contacts.find((contact) => contact.contact_type === "support"); const sendFeedback = (messageBody) => { sendFeedbackProviderAPI({ ...messageBody, - providerEmail: providerSupport.email, + providerEmail: service.contacts.email, serviceId: service.service_id, }); }; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js index e4c30da11..452376cfe 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Details/index.js @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { withStyles } from "@mui/styles"; import Typography from "@mui/material/Typography"; import { useDispatch, useSelector } from "react-redux"; -import isEmpty from "lodash/isEmpty"; import StyledButton from "../../../../../../../../common/StyledButton"; import { useStyles } from "./styles"; @@ -35,11 +34,7 @@ const Details = ({ classes, handleClose, orderType, handleNextSection }) => { const walletList = useSelector((state) => state.userReducer.walletList); const generalWallet = walletList.find((wallet) => wallet.type === walletTypes.GENERAL); const { usd_agi_rate } = useSelector((state) => state.paymentReducer); - const groupInfo = useSelector((state) => { - return state.serviceDetailsReducer.details.groups.find((group) => { - return !isEmpty(group.endpoints.find((endpoint) => endpoint.is_available === 1)); - }); - }); + const groupInfo = useSelector((state) => state.serviceDetailsReducer.details.groupInfo); useEffect(() => { dispatch(paymentActions.fetchUSDConversionRate()); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/index.js index 026c07357..1430236aa 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/index.js @@ -14,7 +14,6 @@ import { isEmpty } from "lodash"; import PaymentInfoCard from "../../PaymentInfoCard"; import AlertBox, { alertTypes } from "../../../../../../common/AlertBox"; import { userActions } from "../../../../../../../Redux/actionCreators"; -import { groupInfo } from "../../../../../../../Redux/reducers/ServiceDetailsReducer"; import CircularProgress from "@material-ui/core/CircularProgress"; import { initPaypalSdk } from "../../../../../../../utility/sdk"; @@ -31,7 +30,7 @@ const GeneralAccountWallet = ({ classes, handleContinue }) => { const dispatch = useDispatch(); const { orgId } = useParams(); - const group = useSelector((state) => groupInfo(state)); + const group = useSelector((state) => state.serviceDetailsReducer.details.groupInfo); const inProgressOrderType = useSelector((state) => state.paymentReducer.paypalInProgress.orderType); const walletList = useSelector((state) => state.userReducer.walletList); const channelInfo = getChannelInfo(walletList); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx index a1a4f222c..ff0f37cf3 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/ContinueButton.jsx @@ -9,7 +9,6 @@ const ContinueButton = ({classes, isServiceAvailable, isContinueEnabled, handleS const tooltipText = "Service is currently offline. Please try after sometime"; const showTooltip = !isServiceAvailable; const isDisabled = !isServiceAvailable || !isContinueEnabled; -console.log("ContinueButton: ", isServiceAvailable, isContinueEnabled); return ( { const dispatch = useDispatch(); const paymentChannelManagementRef = useRef(); - const { price_in_cogs } = useSelector((state) => getPricing(state)); - const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); - + const { org_id, service_id, pricing } = useSelector((state) => state.serviceDetailsReducer.details); + const { price_in_cogs } = pricing; const servicePriceInToken = useMemo(() => cogsToToken(price_in_cogs), [price_in_cogs]); const [mpeBalance, setMpeBalance] = useState(""); const [selectedPayType, setSelectedPayType] = useState(payTypes.CHANNEL_BALANCE); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js index b7bb9c3e7..f91500cea 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js @@ -11,7 +11,6 @@ import ThirdPartyServiceErrorBoundary from "./ThirdPartyServiceErrorBoundary"; import { channelInfo } from "../../../../Redux/reducers/UserReducer"; import { isEmpty } from "lodash"; import { modelStatus } from "../../../../Redux/reducers/ServiceTrainingReducer"; -import { groupInfo } from "../../../../Redux/reducers/ServiceDetailsReducer"; class ThirdPartyAIService extends Component { state = { @@ -119,7 +118,7 @@ const mapStateToProps = (state) => ({ email: state.userReducer.email, wallet: state.userReducer.wallet, channelInfo: channelInfo(state.userReducer.walletList), - groupInfo: groupInfo(state), + groupInfo: state.serviceDetailsReducer.details.groupInfo, freeCallsAvailable: state.serviceDetailsReducer.freeCalls.freeCallsAvailable, }); diff --git a/src/components/ServiceDetails/CreatorDetails/Contacts/index.js b/src/components/ServiceDetails/CreatorDetails/Contacts/index.js index 0272daf1d..4767945fd 100644 --- a/src/components/ServiceDetails/CreatorDetails/Contacts/index.js +++ b/src/components/ServiceDetails/CreatorDetails/Contacts/index.js @@ -1,6 +1,5 @@ import React, { Fragment } from "react"; import PropTypes from "prop-types"; -import isEmpty from "lodash/isEmpty"; import Modal from "@mui/material/Modal"; import Card from "@mui/material/Card"; import { withStyles } from "@mui/styles"; @@ -13,18 +12,10 @@ import CloseIcon from "@mui/icons-material/Close"; import { useStyles } from "./styles"; import AlertBox from "../../../common/AlertBox"; +import { useSelector } from "react-redux"; -const ContactTypes = { - SUPPORT: "support", -}; - -const Contacts = ({ contacts, show, handleClose, classes }) => { - if (isEmpty(contacts)) { - return null; - } - const supportContact = contacts.find((el) => - el.contact_type ? el.contact_type.toLowerCase().trim() === ContactTypes.SUPPORT : null - ); +const Contacts = ({ show, handleClose, classes }) => { + const supportContact = useSelector((state) => state.serviceDetailsReducer.details.contacts); if (!supportContact) { return ( @@ -84,13 +75,6 @@ const Contacts = ({ contacts, show, handleClose, classes }) => { Contacts.propTypes = { show: PropTypes.bool.isRequired, handleClose: PropTypes.func.isRequired, - contacts: PropTypes.arrayOf( - PropTypes.shape({ - phone: PropTypes.string, - email_id: PropTypes.string, - contact_type: PropTypes.string, - }) - ), classes: PropTypes.object.isRequired, }; diff --git a/src/components/ServiceDetails/CreatorDetails/index.js b/src/components/ServiceDetails/CreatorDetails/index.js index 5267ea4af..8bffcba6e 100644 --- a/src/components/ServiceDetails/CreatorDetails/index.js +++ b/src/components/ServiceDetails/CreatorDetails/index.js @@ -8,7 +8,7 @@ import SingularityLogo from "../../../assets/images/avatar.png"; import { useStyles } from "./styles"; import Contacts from "./Contacts"; -const CreatorDetails = ({ classes, organizationName, orgImg, contacts }) => { +const CreatorDetails = ({ classes, organizationName, orgImg }) => { const [showContacts, setShowContacts] = useState(false); return ( @@ -25,7 +25,7 @@ const CreatorDetails = ({ classes, organizationName, orgImg, contacts }) => { Contact
- setShowContacts(false)} /> + setShowContacts(false)} />
); }; @@ -33,13 +33,6 @@ const CreatorDetails = ({ classes, organizationName, orgImg, contacts }) => { CreatorDetails.propTypes = { orgImg: PropTypes.string, organizationName: PropTypes.string, - contacts: PropTypes.arrayOf( - PropTypes.shape({ - phone: PropTypes.string, - email_id: PropTypes.string, - contact_type: PropTypes.string, - }) - ), }; export default withStyles(useStyles)(CreatorDetails); diff --git a/src/components/ServiceDetails/PricingDetails/index.js b/src/components/ServiceDetails/PricingDetails/index.js index 4ade3c8ed..78452ba4f 100644 --- a/src/components/ServiceDetails/PricingDetails/index.js +++ b/src/components/ServiceDetails/PricingDetails/index.js @@ -7,11 +7,10 @@ import StyledButton from "../../common/StyledButton"; import { useStyles } from "./styles"; import Price from "./Price"; import { PricingStrategy } from "../../../utility/PricingStrategy"; -import { pricing as getPricing } from "../../../Redux/reducers/ServiceDetailsReducer"; import { useSelector } from "react-redux"; const PricingDetails = ({ classes, serviceAvailable, handleDemoClick }) => { - const pricing = useSelector((state) => getPricing(state)); + const pricing = useSelector((state) => state.serviceDetailsReducer.details.pricing); const price_strategy = new PricingStrategy(pricing); const priceInAGI = typeof price_strategy === "undefined" ? undefined : price_strategy.getMaxPriceInAGI(); const price_model = typeof price_strategy === "undefined" ? undefined : price_strategy.getPriceModel(); diff --git a/src/components/ServiceDetails/index.js b/src/components/ServiceDetails/index.js index 4ff2c82f3..d633fbab0 100644 --- a/src/components/ServiceDetails/index.js +++ b/src/components/ServiceDetails/index.js @@ -16,12 +16,7 @@ import AboutService from "./AboutService"; import InstallAndRunService from "./InstallAndRunService"; import NotificationBar, { notificationBarTypes } from "../common/NotificationBar"; -import { - fetchTrainingModel, - fetchServiceDetails, - getIsTrainingAvailable, -} from "../../Redux/actionCreators/ServiceDetailsActions"; -import { serviceDetails as getServiceDetails } from "../../Redux/reducers/ServiceDetailsReducer"; +import { fetchServiceDetails, getIsTrainingAvailable } from "../../Redux/actionCreators/ServiceDetailsActions"; import ErrorBox from "../common/ErrorBox"; import SeoMetadata from "../common/SeoMetadata"; @@ -46,7 +41,7 @@ const ServiceDetails = ({ classes }) => { const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const detailsTraining = useSelector((state) => state.serviceDetailsReducer.detailsTraining); - const service = useSelector((state) => getServiceDetails(state, orgId, serviceId)); + const service = useSelector((state) => state.serviceDetailsReducer.details); const loading = useSelector((state) => state.loaderReducer.app.loading); const [activeTab, setActiveTab] = useState(tabId ? tabId : 0); @@ -59,11 +54,16 @@ const ServiceDetails = ({ classes }) => { if (process.env.REACT_APP_SANDBOX) { return; } - if (isEmpty(service) || !service) { - dispatch(fetchServiceDetails(orgId, serviceId)); - } - dispatch(fetchTrainingModel(orgId, serviceId)); - }, [dispatch, orgId, serviceId, service]); + const updateServiceDetails = async () => { + console.log("updateServiceDetails"); + const { org_id, service_id } = service; + if (!serviceId || org_id !== orgId || service_id !== serviceId) { + await dispatch(fetchServiceDetails(orgId, serviceId)); + } + }; + + updateServiceDetails(); + }, [dispatch, orgId, serviceId]); const handleTabChange = (activeTab) => { if (window.location.href.indexOf("#demo") > -1) { diff --git a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/styles.js b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/styles.js index eff0e2200..5ba5789bc 100644 --- a/src/components/UserProfile/UserProfileAccount/MetamaskDetails/styles.js +++ b/src/components/UserProfile/UserProfileAccount/MetamaskDetails/styles.js @@ -46,7 +46,6 @@ export const useStyles = (theme) => ({ }, }, walletId: { - fontSize: "14px !important", wordBreak: "break-all", }, bgBox: { From 033045ab2d2fcd75e3abd61082a129cee37039ad Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 12:00:02 +0300 Subject: [PATCH 44/83] [SM-43] fix serviceDetail components prop drilling --- .../ServiceDetails/CreatorDetails/index.js | 10 +++++++--- .../InstallAndRunService/index.js | 9 ++------- .../ServiceDetails/ProjectDetails/index.js | 18 ++++++++++-------- .../ServiceDetails/TrainingModels/index.js | 11 +++-------- src/components/ServiceDetails/index.js | 17 ++++------------- 5 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/components/ServiceDetails/CreatorDetails/index.js b/src/components/ServiceDetails/CreatorDetails/index.js index 8bffcba6e..1c0a0eb8f 100644 --- a/src/components/ServiceDetails/CreatorDetails/index.js +++ b/src/components/ServiceDetails/CreatorDetails/index.js @@ -7,9 +7,14 @@ import Avatar from "@mui/material/Avatar"; import SingularityLogo from "../../../assets/images/avatar.png"; import { useStyles } from "./styles"; import Contacts from "./Contacts"; +import { useSelector } from "react-redux"; -const CreatorDetails = ({ classes, organizationName, orgImg }) => { +const CreatorDetails = ({ classes }) => { const [showContacts, setShowContacts] = useState(false); + const { organization_name: organizationName, org_assets_url } = useSelector( + (state) => state.serviceDetailsReducer.details + ); + const orgImg = org_assets_url?.hero_image; return (
@@ -31,8 +36,7 @@ const CreatorDetails = ({ classes, organizationName, orgImg }) => { }; CreatorDetails.propTypes = { - orgImg: PropTypes.string, - organizationName: PropTypes.string, + classes: PropTypes.object.isRequired, }; export default withStyles(useStyles)(CreatorDetails); diff --git a/src/components/ServiceDetails/InstallAndRunService/index.js b/src/components/ServiceDetails/InstallAndRunService/index.js index e5c0dede5..18c736a47 100644 --- a/src/components/ServiceDetails/InstallAndRunService/index.js +++ b/src/components/ServiceDetails/InstallAndRunService/index.js @@ -9,7 +9,7 @@ import Card from "../../common/Card"; import { tabNames } from "./TabsMeta"; import IntegrationFilesActions from "./IntegrationFilesActions"; -const InstallAndRunService = ({ classes, service }) => { +const InstallAndRunService = ({ classes }) => { const [activeTab, setActiveTab] = useState(0); const handleTabChange = (newActiveTab) => { @@ -38,12 +38,7 @@ const InstallAndRunService = ({ classes, service }) => { /> - + ); diff --git a/src/components/ServiceDetails/ProjectDetails/index.js b/src/components/ServiceDetails/ProjectDetails/index.js index e627e6c34..1307565bb 100644 --- a/src/components/ServiceDetails/ProjectDetails/index.js +++ b/src/components/ServiceDetails/ProjectDetails/index.js @@ -8,8 +8,16 @@ import ProjectURL from "./ProjectURL"; import Contributors from "./Contibutors"; import { Grid } from "@mui/material"; import Card from "../../common/Card"; +import { useSelector } from "react-redux"; + +const ProjectDetails = ({ classes }) => { + const { + url: projectURL, + contributors, + org_id: orgId, + service_id: serviceId, + } = useSelector((state) => state.serviceDetailsReducer.details); -const ProjectDetails = ({ classes, projectURL, contributors, orgId, serviceId }) => { const data = [ { label: "Project URL", value: }, { label: "Organization ID", value: orgId }, @@ -43,13 +51,7 @@ const ProjectDetails = ({ classes, projectURL, contributors, orgId, serviceId }) }; ProjectDetails.propTypes = { - projectURL: PropTypes.string, - contributors: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string, - email_id: PropTypes.string, - }) - ), + classes: PropTypes.object, }; export default withStyles(useStyles)(ProjectDetails); diff --git a/src/components/ServiceDetails/TrainingModels/index.js b/src/components/ServiceDetails/TrainingModels/index.js index 4875ad565..a12ef419b 100644 --- a/src/components/ServiceDetails/TrainingModels/index.js +++ b/src/components/ServiceDetails/TrainingModels/index.js @@ -15,7 +15,7 @@ import { useDispatch } from "react-redux"; import { resetCurrentModelDetails } from "../../../Redux/actionCreators/ServiceTrainingActions"; import { useLocation } from "react-router-dom"; -const TrainingModels = ({ classes, service }) => { +const TrainingModels = ({ classes }) => { const { state } = useLocation(); const [showCreateModel, setShowCreateModel] = useState( @@ -66,7 +66,7 @@ const TrainingModels = ({ classes, service }) => { {showCreateModel ? ( - + ) : ( @@ -75,12 +75,7 @@ const TrainingModels = ({ classes, service }) => { )} - + ); diff --git a/src/components/ServiceDetails/index.js b/src/components/ServiceDetails/index.js index d633fbab0..6df153f71 100644 --- a/src/components/ServiceDetails/index.js +++ b/src/components/ServiceDetails/index.js @@ -55,7 +55,6 @@ const ServiceDetails = ({ classes }) => { return; } const updateServiceDetails = async () => { - console.log("updateServiceDetails"); const { org_id, service_id } = service; if (!serviceId || org_id !== orgId || service_id !== serviceId) { await dispatch(fetchServiceDetails(orgId, serviceId)); @@ -63,7 +62,7 @@ const ServiceDetails = ({ classes }) => { }; updateServiceDetails(); - }, [dispatch, orgId, serviceId]); + }, [dispatch, orgId, serviceId, service]); const handleTabChange = (activeTab) => { if (window.location.href.indexOf("#demo") > -1) { @@ -100,21 +99,13 @@ const ServiceDetails = ({ classes }) => { name: "About", activeIndex: 0, tabId: "serviceDemo", - component: ( - - ), + component: , }, { name: "Install and Run", tabId: "serviceGuides", activeIndex: 1, - component: , //TODO remove service attribute + component: , }, ]; @@ -132,7 +123,7 @@ const ServiceDetails = ({ classes }) => { name: "Models", tabId: "serviceTraining", activeIndex: 3, - component: , + component: , }); } From ac4e77312ea0b791a6a32e97a9579768fbffc207 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 12:01:28 +0300 Subject: [PATCH 45/83] [SM-43] optimize ServiceOverview component --- .../AboutService/ServiceOverview.js | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/ServiceOverview.js b/src/components/ServiceDetails/AboutService/ServiceOverview.js index d2f35306d..d10397763 100644 --- a/src/components/ServiceDetails/AboutService/ServiceOverview.js +++ b/src/components/ServiceDetails/AboutService/ServiceOverview.js @@ -1,4 +1,6 @@ -import React from "react"; +import React, { useMemo } from "react"; +import { useSelector } from "react-redux"; +import PropTypes from "prop-types"; import { withStyles } from "@mui/styles"; import parseHtml from "html-react-parser"; @@ -7,45 +9,46 @@ import Tags from "../../common/Tags"; import Card from "../../common/Card"; import { Link, useLocation } from "react-router-dom"; -const ServiceOverview = ({ classes, description, tags, isTrainingAvailable }) => { +const ServiceOverview = ({ classes, isTrainingAvailable }) => { const location = useLocation(); + const { description, tags } = useSelector((state) => state.serviceDetailsReducer.details); - const parseDescription = (description) => { - if (description.startsWith("
")) { - return parseHtml(description); - } - return description; - }; + const parsedDescription = useMemo(() => { + return description.startsWith("
") ? parseHtml(description) : description; + }, [description]); - const renderSandboxInfo = () => { - if (process.env.REACT_APP_SANDBOX) { - return ( - <> -

After testing your component you can package your component with the below command

- npm run zip-components - - ); - } - return null; + const SandboxInfo = () => { + if (!process.env.REACT_APP_SANDBOX) return null; + return ( + <> +

After testing your component you can package your component with the below command

+ npm run zip-components + + ); }; + const trainingLinkPath = useMemo(() => { + return location.pathname.split("tab/")[0] + "tab/" + 3; + }, [location.pathname]); + + const TrainingLink = ({ path }) => ( +
+

For this service you can create your own training model!

+ + Try now! + +
+ ); + return ( - {renderSandboxInfo()} -

{parseDescription(description)}

+ +

{parsedDescription}

- {Boolean(isTrainingAvailable) && ( -
-

For this service you can create your own training model!

- {/* //TODO */} - - Try now! - -
- )} + {isTrainingAvailable && } } /> @@ -53,3 +56,8 @@ const ServiceOverview = ({ classes, description, tags, isTrainingAvailable }) => }; export default withStyles(useStyles)(ServiceOverview); + +ServiceOverview.propTypes = { + classes: PropTypes.object.isRequired, + isTrainingAvailable: PropTypes.bool.isRequired, +}; From e0fa3e2a0110607c74aeb312128dcdebb53d0f73 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 12:02:54 +0300 Subject: [PATCH 46/83] [SM-43] fix prop drilling in AboutService component --- .../AboutService/DemoToggler.js | 22 ++++---- .../ServiceDetails/AboutService/index.js | 50 +++---------------- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/DemoToggler.js b/src/components/ServiceDetails/AboutService/DemoToggler.js index 008fc612e..615eec439 100644 --- a/src/components/ServiceDetails/AboutService/DemoToggler.js +++ b/src/components/ServiceDetails/AboutService/DemoToggler.js @@ -10,21 +10,23 @@ import { useStyles } from "./styles"; import FeedbackFormModal from "../../FeedbackFormModal/FeedbackFormModal"; import { sendFeedbackProviderAPI } from "../../../config/SupportAPI"; import UnauthenticatedDummyToggler from "../../common/UnauthenticatedDummyToggler"; +import { useSelector } from "react-redux"; -const DemoToggler = ({ - classes, - service, - serviceAvailable, - // scrollToView, - demoComponentRequired, -}) => { +const DemoToggler = ({ classes }) => { const [isModalVisible, setIsModalVisible] = useState(false); + const { + service_id, + contacts, + demo_component_required: demoComponentRequired, + is_available: serviceAvailable, + } = useSelector((state) => state.serviceDetailsReducer.details); + const supportContactEmail = contacts.email; const sendFeedback = (messageBody) => { sendFeedbackProviderAPI({ ...messageBody, - providerEmail: service.contacts.email, - serviceId: service.service_id, + providerEmail: supportContactEmail, + serviceId: service_id, }); }; @@ -67,7 +69,7 @@ const DemoToggler = ({ return ; } - return ; + return ; }; return ( diff --git a/src/components/ServiceDetails/AboutService/index.js b/src/components/ServiceDetails/AboutService/index.js index 894da7bb3..693a64088 100644 --- a/src/components/ServiceDetails/AboutService/index.js +++ b/src/components/ServiceDetails/AboutService/index.js @@ -6,39 +6,15 @@ import DemoToggler from "./DemoToggler"; import ServiceOverview from "./ServiceOverview"; import CreatorDetails from "../CreatorDetails"; import ProjectDetails from "../ProjectDetails"; -import MediaGallery from "../MediaGallery"; import PromoBox from "./PromoBox"; import Card from "../../common/Card"; -const AboutService = ({ - classes, - service, - serviceAvailable, - // scrollToView, - demoComponentRequired, - isTrainingAvailable, -}) => { +const AboutService = ({ classes, isTrainingAvailable }) => { return ( - - - } - /> + + } /> {/* */} {!process.env.REACT_APP_SANDBOX && (
@@ -47,23 +23,9 @@ const AboutService = ({ )} - - } - /> - - + } /> + + {/* */} {!process.env.REACT_APP_SANDBOX && (
From 79b310a52c2b9ad17649cd61b52eef0bb68c75af Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 12:03:47 +0300 Subject: [PATCH 47/83] [SM-43] optimize ThirdPartyAIService component --- .../ServiceDemo/ThirdPartyAIService.js | 172 ++++++++---------- .../ThirdPartyServiceErrorBoundary.js | 9 +- 2 files changed, 88 insertions(+), 93 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js index f91500cea..793029579 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyAIService.js @@ -1,63 +1,75 @@ -import React, { Component, Suspense } from "react"; -import { connect } from "react-redux"; +import React, { Suspense, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { withStyles } from "@mui/styles"; import thirdPartyCustomUIComponents from "../../../../assets/thirdPartyServices"; import { useStyles } from "./styles"; -import { serviceActions, loaderActions } from "../../../../Redux/actionCreators"; import CompletedActions from "./CompletedActions"; import { createServiceClient, callTypes } from "../../../../utility/sdk"; import ThirdPartyServiceErrorBoundary from "./ThirdPartyServiceErrorBoundary"; -import { channelInfo } from "../../../../Redux/reducers/UserReducer"; import { isEmpty } from "lodash"; import { modelStatus } from "../../../../Redux/reducers/ServiceTrainingReducer"; +import { fetchFeedback } from "../../../../Redux/actionCreators/ServiceActions"; -class ThirdPartyAIService extends Component { - state = { - feedback: { - comment: "", - rating: "", - }, - loading: true, - }; +const ThirdPartyAIService = ({ + classes, + isServiceExecutionComplete, + onStart, + onComplete, + onError, + handleResetAndRun, +}) => { + const dispatch = useDispatch(); + const { service_id, org_id, groupInfo } = useSelector((state) => state.serviceDetailsReducer.details); + const freeCallsAvailable = useSelector((state) => state.serviceDetailsReducer.freeCalls.freeCallsAvailable); + const wallet = useSelector((state) => state.userReducer.wallet); + const { modelsList, modelId: selectedModelId } = useSelector((state) => state.serviceTrainingReducer); - componentDidMount = async () => { - const { org_id, service_id, freeCallsAvailable, groupInfo, wallet } = this.props; - const callType = freeCallsAvailable > 0 ? callTypes.FREE : callTypes.REGULAR; - this.serviceClient = await createServiceClient( - org_id, - service_id, - groupInfo, - this.props.serviceRequestStartHandler, - this.props.serviceRequestCompleteHandler, - this.props.serviceRequestErrorHandler, - callType, - wallet - ); - this.setupComponent(); - this.setState({ loading: false, callType }); - }; + const [feedback, setFeedback] = useState({ + comment: "", + rating: "", + }); + const [callType, setCallType] = useState(); + const [serviceClient, setServiceClient] = useState(); + + useEffect(() => { + const getServiceClient = async () => { + const callType = freeCallsAvailable > 0 ? callTypes.FREE : callTypes.REGULAR; + const newServiceClient = await createServiceClient( + org_id, + service_id, + groupInfo, + onStart, + onComplete, + onError, + callType, + wallet + ); + setServiceClient(newServiceClient); + }; + getServiceClient(); + setupComponent(); + setCallType(callType); + }, [org_id, service_id]); - setupComponent = () => { + const setupComponent = () => { if (process.env.REACT_APP_SANDBOX) { return; } - this.fetchUserFeedback(); + fetchUserFeedback(); }; - fetchUserFeedback = async () => { - const { org_id, service_id } = this.props; - const feedback = await this.props.fetchFeedback(org_id, service_id); + const fetchUserFeedback = async () => { + const feedback = await dispatch(fetchFeedback(org_id, service_id)); if (!feedback.data?.length > 0) { return; } - this.setState({ feedback: { comment: feedback.data[0].comment[0], rating: feedback.data[0].rating } }); + const feedbackData = feedback.data[0]; + setFeedback({ comment: feedbackData.comment[0], rating: feedbackData.rating }); }; - getModelsIds() { - const modelsList = this.props.modelsList; - + const getModelsIds = () => { if (isEmpty(modelsList)) { return []; } @@ -69,62 +81,38 @@ class ThirdPartyAIService extends Component { label: model.modelName, }; }); - } - - render() { - const { loading } = this.state; - if (loading) { - return null; - } - - const { selectedModelId, org_id, service_id, classes, stopLoader, isServiceExecutionComplete, handleResetAndRun } = - this.props; - const { feedback, callType } = this.state; - const { serviceClient } = this; - const AIServiceCustomComponent = thirdPartyCustomUIComponents.componentFor(org_id, service_id); - const modelsIds = this.getModelsIds(); + }; - return ( -
- Loading Service...
}> - - - - - -
- ); + const AIServiceCustomComponent = thirdPartyCustomUIComponents.componentFor(org_id, service_id); + const modelsIds = getModelsIds(); + if (isEmpty(serviceClient) || !serviceClient) { + return
Loading Service...
; } -} - -const mapStateToProps = (state) => ({ - selectedModelId: state.serviceTrainingReducer.currentModel.modelId, - modelsList: state.serviceTrainingReducer.modelsList, - isComplete: state.serviceReducer.serviceMethodExecution.isComplete, - email: state.userReducer.email, - wallet: state.userReducer.wallet, - channelInfo: channelInfo(state.userReducer.walletList), - groupInfo: state.serviceDetailsReducer.details.groupInfo, - freeCallsAvailable: state.serviceDetailsReducer.freeCalls.freeCallsAvailable, -}); -const mapDispatchToProps = (dispatch) => ({ - fetchFeedback: (orgId, serviceId) => dispatch(serviceActions.fetchFeedback(orgId, serviceId)), - stopLoader: () => dispatch(loaderActions.startAppLoader), -}); + return ( +
+ Loading Service...
}> + + + + + +
+ ); +}; -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(useStyles)(ThirdPartyAIService)); +export default withStyles(useStyles)(ThirdPartyAIService); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyServiceErrorBoundary.js b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyServiceErrorBoundary.js index d4838475a..efe8be75c 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyServiceErrorBoundary.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/ThirdPartyServiceErrorBoundary.js @@ -1,6 +1,9 @@ import React, { Component } from "react"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; +import { connect } from "react-redux"; +import { stopAppLoader } from "../../../../Redux/actionCreators/LoaderActions"; + class ThirdPartyServiceErrorBoundary extends Component { state = { error: undefined, @@ -27,4 +30,8 @@ class ThirdPartyServiceErrorBoundary extends Component { } } -export default ThirdPartyServiceErrorBoundary; +const mapDispatchToProps = (dispatch) => ({ + stopLoader: () => dispatch(stopAppLoader()), +}); + +export default connect(undefined, mapDispatchToProps)(ThirdPartyServiceErrorBoundary); From 259a03e23b2c1ab75bee3f51d50235f1de819abe Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 12:38:08 +0300 Subject: [PATCH 48/83] [SM-103] update version of snet-sdk-web --- package.json | 2 +- src/utility/PaymentChannelManagement.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4c093adb2..ab8b533f1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "redux-thunk": "^3.1.0", "singularitynet-platform-contracts": "2.1.0", "slick-carousel": "^1.8.1", - "snet-sdk-web": "5.0.3", + "snet-sdk-web": "5.0.4", "utf8": "^3.0.0", "validate.js": "^0.13.1", "web3": "^4.11.1" diff --git a/src/utility/PaymentChannelManagement.js b/src/utility/PaymentChannelManagement.js index 1e18fd0b2..24357648b 100644 --- a/src/utility/PaymentChannelManagement.js +++ b/src/utility/PaymentChannelManagement.js @@ -1,7 +1,7 @@ import find from "lodash/find"; import minBy from "lodash/minBy"; import isEmpty from "lodash/isEmpty"; -import { PaymentChannelProvider } from "snet-sdk-core/mpe"; +import { PaymentChannelProvider } from "snet-sdk-web/mpe"; const ONE_YEAR_BLOCKS = 2102400; From bbfb50ad30789e5f82ffeab8f9f50820709b53c3 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 15:02:57 +0300 Subject: [PATCH 49/83] [SM-43] optimize expired session component --- .../PaymentPopup/Purchase/index.js | 4 +- .../MetamaskFlow/PaymentOptions.jsx | 2 +- .../ExpiredSession/MetamaskFlow/index.js | 10 ++- .../Purchase/ExpiredSession/index.js | 85 +------------------ 4 files changed, 12 insertions(+), 89 deletions(-) diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Purchase/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Purchase/index.js index 5f2e9bc56..9c9cca7c2 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Purchase/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/GeneralAccountWallet/PaymentPopup/Purchase/index.js @@ -10,7 +10,7 @@ import { alertTypes } from "../../../../../../../../common/AlertBox"; import { useDispatch, useSelector } from "react-redux"; import { paymentActions } from "../../../../../../../../../Redux/actionCreators"; -const Purchase = ({ classes, handleCancel, handleNext, setAmount }) => { +const PaymentPopup = ({ classes, handleCancel, handleNext, setAmount }) => { const dispatch = useDispatch(); const paypalInProgress = useSelector((state) => state.paymentReducer.paypalInProgress); @@ -88,4 +88,4 @@ const Purchase = ({ classes, handleCancel, handleNext, setAmount }) => {
); }; -export default withStyles(useStyles)(Purchase); +export default withStyles(useStyles)(PaymentPopup); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx index 33a1eb40f..9c7e81e55 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/PaymentOptions.jsx @@ -109,7 +109,7 @@ PaymentOptions.propTypes = { classes: PropTypes.object.isRequired, setNoOfServiceCalls: PropTypes.func.isRequired, setSelectedPayType: PropTypes.func.isRequired, - noOfServiceCalls: PropTypes.number.isRequired, + noOfServiceCalls: PropTypes.string.isRequired, totalPrice: PropTypes.string.isRequired, mpeBalance: PropTypes.string.isRequired, servicePrice: PropTypes.string.isRequired, diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js index 3346a8f5d..c8d3a968e 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/MetamaskFlow/index.js @@ -19,10 +19,15 @@ import ContinueButton from "./ContinueButton"; import DepositButton from "./DepositButton"; import PaymentOptions from "./PaymentOptions"; -const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall, isServiceAvailable }) => { +const MetamaskFlow = ({ classes, handleContinue, setIsLastPaidCall }) => { const dispatch = useDispatch(); const paymentChannelManagementRef = useRef(); - const { org_id, service_id, pricing } = useSelector((state) => state.serviceDetailsReducer.details); + const { + org_id, + service_id, + pricing, + is_available: isServiceAvailable, + } = useSelector((state) => state.serviceDetailsReducer.details); const { price_in_cogs } = pricing; const servicePriceInToken = useMemo(() => cogsToToken(price_in_cogs), [price_in_cogs]); const [mpeBalance, setMpeBalance] = useState(""); @@ -191,5 +196,4 @@ MetamaskFlow.propTypes = { classes: PropTypes.object.isRequired, handleContinue: PropTypes.func.isRequired, setIsLastPaidCall: PropTypes.func.isRequired, - isServiceAvailable: PropTypes.bool.isRequired, }; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/index.js index a0b82507a..83378bcb6 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ExpiredSession/index.js @@ -1,102 +1,21 @@ import React, { useEffect } from "react"; import { withStyles } from "@mui/styles"; -// import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet"; import Typography from "@mui/material/Typography"; -// import { useSelector } from "react-redux"; - import { useStyles } from "./styles"; -// import StyledDropdown from "../../../../../common/StyledDropdown"; -// import { walletTypes } from "../../../../../../Redux/actionCreators/UserActions"; -// import { userActions, loaderActions, paymentActions } from "../../../../../../Redux/actionCreators"; -// import WalletDetailsToggler from "./WalletDetailsToggler"; -// import { LoaderContent } from "../../../../../../utility/constants/LoaderContent"; -// import { useLocation, useParams } from "react-router-dom"; -// import queryString from "query-string"; import MetamaskFlow from "./MetamaskFlow"; -const ExpiredSession = ({ classes, setIsLastPaidCall, handleComplete, isServiceAvailable }) => { - // const dispatch = useDispatch(); - // const { orderId, paymentId } = useParams(); - // const location = useLocation(); - - // const wallet = useSelector((state) => state.userReducer.wallet); - +const ExpiredSession = ({ classes, setIsLastPaidCall, handleComplete }) => { useEffect(() => { setIsLastPaidCall(false); }, [setIsLastPaidCall]); - // const checkForPaymentsInProgress = useCallback(async () => { - // const { paymentId: paypalPaymentId, PayerID } = queryString.parse(location.search); - // if (!(orderId && paymentId && paypalPaymentId && PayerID)) { - // return; - // } - - // try { - // dispatch(loaderActions.startAppLoader(LoaderContent.FETCH_WALLET)); - // const { data } = await dispatch(paymentActions.fetchOrderDetails(orderId)); - // const orderType = data.item_details.order_type; - // dispatch(paymentActions.updatePaypalInProgress(orderId, orderType, paymentId, paypalPaymentId, PayerID)); - // dispatch(userActions.updateWallet({ type: walletTypes.GENERAL })); - // } catch (err) { - // console.error("error in fetching of order details: ", err); - // } finally { - // dispatch(loaderActions.stopAppLoader()); - // } - // }, [dispatch, orderId, paymentId, location.search]); - - // useEffect(() => { - // if (process.env.REACT_APP_SANDBOX) { - // return; - // } - - // try { - // dispatch(loaderActions.startAppLoader(LoaderContent.INIT_SERVICE_DEMO)); - // checkForPaymentsInProgress(); - // dispatch(loaderActions.stopAppLoader()); - // } catch (error) { - // dispatch(loaderActions.stopAppLoader()); - // } - // }, [dispatch, checkForPaymentsInProgress]); - - // const handlePayTypeChange = (event) => { - // const { value } = event.target; - // dispatch(userActions.updateWallet({ type: value })); - // }; - - // const channelPaymentOptions = [ - // { value: walletTypes.GENERAL, label: "PayPal" }, - // { value: walletTypes.METAMASK, label: "Metamask" }, - // ]; - return (
You have run out of free trial. Please select a payment method to continue
- {/*
-
- - Payment Type - - - -
-
*/} - - {/* */} +
); From af872dacb15c0f4c13e6343b19f3bef685dbd6bf Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 15:04:14 +0300 Subject: [PATCH 50/83] [SM-43] optimize service details component --- .../ServiceDemo/PurchaseToggler.js | 12 --- .../AboutService/ServiceDemo/index.js | 82 ++++++++++--------- 2 files changed, 43 insertions(+), 51 deletions(-) delete mode 100644 src/components/ServiceDetails/AboutService/ServiceDemo/PurchaseToggler.js diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/PurchaseToggler.js b/src/components/ServiceDetails/AboutService/ServiceDemo/PurchaseToggler.js deleted file mode 100644 index 5738e7dad..000000000 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/PurchaseToggler.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import ThirdPartyAIService from "./ThirdPartyAIService"; -import Purchase from "./Purchase"; - -const PurchaseToggler = ({ purchaseCompleted, purchaseProps, thirdPartyProps }) => { - if (purchaseCompleted) { - return ; - } - return ; -}; - -export default PurchaseToggler; diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/index.js index 125a6bd1d..caf676176 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/index.js @@ -1,17 +1,19 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { memo, useCallback, useEffect, useState } from "react"; import { withStyles } from "@mui/styles"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import PropTypes from "prop-types"; import { loaderActions } from "../../../../Redux/actionCreators"; import ProgressBar from "../../../common/ProgressBar"; import { useStyles } from "./styles"; -import PurchaseToggler from "./PurchaseToggler"; import { LoaderContent } from "../../../../utility/constants/LoaderContent"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; import Routes from "../../../../utility/constants/Routes"; import { progressTabStatus } from "../../../common/ProgressBar"; import { useLocation } from "react-router-dom"; +import Purchase from "./Purchase"; +import ThirdPartyAIService from "./ThirdPartyAIService"; const demoProgressStatus = { purchasing: 0, @@ -19,20 +21,23 @@ const demoProgressStatus = { displayingResponse: 2, }; -const ServiceDemo = ({ classes, service }) => { +const progressList = [{ label: "Purchase" }, { label: "Configure" }, { label: "Results", status: undefined }]; + +const ServiceDemo = ({ classes }) => { const dispatch = useDispatch(); const location = useLocation(); - const [progressText, setProgressText] = useState([ - { label: "Purchase" }, - { label: "Configure" }, - { label: "Results", status: undefined }, - ]); + const serviceName = useSelector((state) => state.serviceDetailsReducer.details.display_name); + + const [progressText, setProgressText] = useState(progressList); const [purchaseCompleted, setPurchaseCompleted] = useState(false); const [isServiceExecutionComplete, setIsServiceExecutionComplete] = useState(false); const [alert, setAlert] = useState({}); const [isLastPaidCall, setIsLastPaidCall] = useState(false); + const MemoizedProgressBar = memo(ProgressBar); + const MemoizedAlertBox = memo(AlertBox); + useEffect(() => { if (location.hash === Routes.hash.SERVICE_DEMO) { window.scroll({ @@ -50,19 +55,16 @@ const ServiceDemo = ({ classes, service }) => { const serviceRequestStartHandler = () => { setAlert({}); - dispatch(loaderActions.startAppLoader(LoaderContent.SERVICE_INVOKATION(service.display_name))); + dispatch(loaderActions.startAppLoader(LoaderContent.SERVICE_INVOKATION(serviceName))); }; + const updateProgressText = useCallback((status) => { + setProgressText((prev) => prev.map((item) => (item.label === "Results" ? { ...item, status } : item))); + }, []); + const serviceRequestCompleteHandler = () => { setIsServiceExecutionComplete(true); - setProgressText( - progressText.map((item) => { - if (item.label === "Results") { - item.status = progressTabStatus.SUCCESS; - } - return item; - }) - ); + updateProgressText(progressTabStatus.SUCCESS); dispatch(loaderActions.stopAppLoader()); }; @@ -70,7 +72,7 @@ const ServiceDemo = ({ classes, service }) => { setPurchaseCompleted(false); setIsServiceExecutionComplete(false); setAlert({}); - setProgressText(progressText.map((item) => ({ label: item.label }))); + setProgressText(progressList); }; const serviceRequestErrorHandler = (error) => { @@ -90,6 +92,7 @@ const ServiceDemo = ({ classes, service }) => { }; const handlePurchaseError = (error) => { + console.error(error); setPurchaseCompleted(false); setAlert({ type: alertTypes.ERROR, message: "Purchase could not be completed. Please try again" }); dispatch(loaderActions.stopAppLoader()); @@ -99,35 +102,36 @@ const ServiceDemo = ({ classes, service }) => { return (
- + {isLastPaidCall && ( - )} - + {purchaseCompleted ? ( + + ) : ( + + )}
); }; +ServiceDemo.propTypes = { + classes: PropTypes.object.isRequired, +}; + export default withStyles(useStyles)(ServiceDemo); From b0ea22bfcac3e00e25df9bed059861f16a33f590 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 15:04:52 +0300 Subject: [PATCH 51/83] [SM-43] fix prop drilling in active section component --- .../actionCreators/ServiceDetailsActions.js | 70 +++++++++++++------ .../Purchase/ActiveSession/index.js | 8 ++- .../ServiceDemo/Purchase/index.js | 4 +- src/components/common/ErrorBox/index.js | 4 +- 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index ab48a13e2..3a05fb3bf 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -14,7 +14,11 @@ export const UPDATE_FREE_CALLS_INFO = "UPDATE_FREE_CALLS_INFO"; export const UPDATE_TRAINING_DETAILS = "UPDATE_TRAINING_DETAILS"; export const UPDATE_FREECALL_SIGNATURE = "UPDATE_FREECALL_SIGNATURE"; -const resetServiceDetails = (dispatch) => { +const ContactTypes = { + SUPPORT: "support", +}; + +const resetServiceDetails = () => (dispatch) => { dispatch({ type: RESET_SERVICE_DETAILS }); }; @@ -37,18 +41,38 @@ const fetchServiceDetailsAPI = async (orgId, serviceId) => { return response.json(); }; -export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { - try { - dispatch(loaderActions.startAppLoader(LoaderContent.FETCH_SERVICE_DETAILS)); - dispatch(resetServiceDetails); - dispatch(resetCurrentModelDetails()); - dispatch(resetModelList()); - const { data: serviceDetails } = await fetchServiceDetailsAPI(orgId, serviceId); - dispatch(fetchServiceDetailsSuccess(serviceDetails)); - } catch (error) { - dispatch(fetchServiceDetailsFailure(error)); - throw error; +const enhanceGroup = (group) => ({ ...group, endpoints: group.endpoints.map(({ endpoint }) => endpoint) }); + +const parseGroupInfo = (groups) => { + const serviceGroups = groups; + const availableGroup = serviceGroups.find(({ endpoints }) => + endpoints.some((endpoint) => endpoint.is_available === 1) + ); + if (availableGroup) { + return enhanceGroup(availableGroup); + } + const firstGroup = serviceGroups[0]; + if (firstGroup) { + return enhanceGroup(firstGroup); + } +}; + +const parsePricing = (group) => { + if (!group) { + return {}; } + + return group.pricing.find((price) => price.default === true); +}; + +const parseServiceDetails = (service) => { + const groupInfo = parseGroupInfo(service.groups); + return { + ...service, + contacts: service.contacts.find((contact) => contact.contact_type === ContactTypes.SUPPORT), + groupInfo, + pricing: parsePricing(groupInfo), + }; }; const fetchMeteringDataSuccess = (freeCallsAvailable, freeCallsTotal) => (dispatch) => { @@ -65,22 +89,22 @@ const fetchTrainingModelSuccess = (serviceTrainingData) => (dispatch) => { dispatch({ type: UPDATE_TRAINING_DETAILS, payload: serviceTrainingData }); }; -const fetchServiceTrainingDataAPI = async (orgId, serviceId) => { +export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { try { - const dataForUrl = await fetchServiceDetailsAPI(orgId, serviceId); - const url = `${dataForUrl.data.groups[0].endpoints[0].endpoint}/heartbeat`; - const response = await fetch(url); - return response.json(); + dispatch(loaderActions.startAppLoader(LoaderContent.FETCH_SERVICE_DETAILS)); + dispatch(resetServiceDetails()); + dispatch(resetCurrentModelDetails()); + dispatch(resetModelList()); + const { data: serviceDetails } = await fetchServiceDetailsAPI(orgId, serviceId); + const parsedServiceDetails = parseServiceDetails(serviceDetails); + dispatch(fetchServiceDetailsSuccess(parsedServiceDetails)); + dispatch(fetchTrainingModelSuccess(serviceDetails)); } catch (error) { - return {}; + dispatch(fetchServiceDetailsFailure(error)); + throw error; } }; -export const fetchTrainingModel = (orgId, serviceId) => async (dispatch) => { - const serviceTrainingData = await fetchServiceTrainingDataAPI(orgId, serviceId); - dispatch(fetchTrainingModelSuccess(serviceTrainingData)); -}; - const getAvailableFreeCalls = (orgId, serviceId, groupId) => async (dispatch) => { const { signature, diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js index c1ffba297..6773840bc 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/ActiveSession/index.js @@ -12,10 +12,14 @@ import { getTrainingModels } from "../../../../../../Redux/actionCreators/Servic import { isUndefined } from "lodash"; import { updateMetamaskWallet } from "../../../../../../Redux/actionCreators/UserActions"; -const ActiveSession = ({ classes, freeCallsAvailable, handleComplete, freeCallsTotal, isServiceAvailable }) => { +const ActiveSession = ({ classes, freeCallsAvailable, handleComplete, freeCallsTotal }) => { const dispatch = useDispatch(); const { detailsTraining } = useSelector((state) => state.serviceDetailsReducer); - const { org_id, service_id } = useSelector((state) => state.serviceDetailsReducer.details); + const { + org_id, + service_id, + is_available: isServiceAvailable, + } = useSelector((state) => state.serviceDetailsReducer.details); const { modelsList } = useSelector((state) => state.serviceTrainingReducer); const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); const [showTooltip, setShowTooltip] = useState(false); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js index 1da894305..e559de79e 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/Purchase/index.js @@ -10,7 +10,7 @@ import "./styles.css"; import { isUndefined, some } from "lodash"; import { useParams } from "react-router-dom"; -const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, setIsLastPaidCall }) => { +const Purchase = ({ handleComplete, handlePurchaseError, setIsLastPaidCall }) => { const dispatch = useDispatch(); const { orgId, serviceId } = useParams(); const { free_calls, group_id } = useSelector((state) => @@ -70,7 +70,6 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set setIsLastPaidCall={setIsLastPaidCall} handleComplete={handleComplete} handlePurchaseError={handlePurchaseError} - isServiceAvailable={isServiceAvailable} /> ); } @@ -79,7 +78,6 @@ const Purchase = ({ handleComplete, handlePurchaseError, isServiceAvailable, set freeCallsAvailable={freeCalls.freeCallsAvailable} freeCallsTotal={freeCalls.freeCallsTotal} handleComplete={handleComplete} - isServiceAvailable={isServiceAvailable} /> ); }; diff --git a/src/components/common/ErrorBox/index.js b/src/components/common/ErrorBox/index.js index 04638f548..906cf64c1 100644 --- a/src/components/common/ErrorBox/index.js +++ b/src/components/common/ErrorBox/index.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { memo } from "react"; import { withStyles } from "@mui/styles"; import PropTypes from "prop-types"; @@ -23,4 +23,4 @@ ErrorBox.propTypes = { errText: PropTypes.string, }; -export default withStyles(useStyles)(ErrorBox); +export default memo(withStyles(useStyles)(ErrorBox)); From 352928f439ebf68a5d2faac83b07e549be2622f3 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 23 Jun 2025 15:08:22 +0300 Subject: [PATCH 52/83] [SM-103] update sdk-web dependency --- src/utility/PaymentChannelManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility/PaymentChannelManagement.js b/src/utility/PaymentChannelManagement.js index 1e18fd0b2..24357648b 100644 --- a/src/utility/PaymentChannelManagement.js +++ b/src/utility/PaymentChannelManagement.js @@ -1,7 +1,7 @@ import find from "lodash/find"; import minBy from "lodash/minBy"; import isEmpty from "lodash/isEmpty"; -import { PaymentChannelProvider } from "snet-sdk-core/mpe"; +import { PaymentChannelProvider } from "snet-sdk-web/mpe"; const ONE_YEAR_BLOCKS = 2102400; From 2a82a26431a5a2e18acb2718a19c421a3dd83857 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:22:45 +0300 Subject: [PATCH 53/83] [SM-139] update Styled dropdown label style --- src/components/common/StyledDropdown/styles.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/common/StyledDropdown/styles.js b/src/components/common/StyledDropdown/styles.js index a20f772c7..90f084d01 100644 --- a/src/components/common/StyledDropdown/styles.js +++ b/src/components/common/StyledDropdown/styles.js @@ -1,6 +1,12 @@ import { makeStyles } from "@mui/styles"; export const useStyles = makeStyles((theme) => ({ + formControl: { + "& .MuiFormLabel-root": { + backgroundColor: theme.palette.text.white, + padding: "0 4px", + }, + }, selectEmpty: { fontFamily: theme.typography.primary.main, color: `${theme.palette.text.dialogTitle} !important`, From c2187090990c3a3983ec48b99e12712a5cd5b2d1 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:23:15 +0300 Subject: [PATCH 54/83] [SM-139] remove user profile image dump --- src/components/common/UserProfileCard/index.js | 7 +------ src/components/common/UserProfileCard/styles.js | 8 -------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/components/common/UserProfileCard/index.js b/src/components/common/UserProfileCard/index.js index 551e04125..0528cb049 100644 --- a/src/components/common/UserProfileCard/index.js +++ b/src/components/common/UserProfileCard/index.js @@ -2,18 +2,13 @@ import React from "react"; import { withStyles } from "@mui/styles"; import CloseIcon from "@mui/icons-material/Close"; import PropTypes from "prop-types"; -import Icon from "@mui/material/Icon"; -import clsx from "clsx"; import { useStyles } from "./styles"; const UserProfileCard = ({ classes, nickName, onClose }) => { return (
- -
-

{nickName}

-
+

{nickName}

{onClose && }
); diff --git a/src/components/common/UserProfileCard/styles.js b/src/components/common/UserProfileCard/styles.js index fe80fd027..7bbec9312 100644 --- a/src/components/common/UserProfileCard/styles.js +++ b/src/components/common/UserProfileCard/styles.js @@ -1,20 +1,12 @@ export const useStyles = (theme) => ({ Userdetails: { display: "flex", - marginBottom: 10, "& span": { color: theme.palette.text.lightShadedGray, fontSize: 66, }, "& div": { marginLeft: 22, - "& h4": { - fontWeight: 600, - margin: 0, - color: theme.palette.text.black1, - lineHeight: "27px", - fontSize: 20, - }, "& a": { color: theme.palette.text.lightShadedGray, fontSize: 16, From 7efc0155cf165f7555bba44cdc395e65a1574290 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:24:08 +0300 Subject: [PATCH 55/83] [SM-139] update gaps between some elements --- src/components/Onboarding/TermsOfUse/styles.js | 2 +- .../ServiceDemo/CompletedActions/styles.js | 9 +++------ .../AboutService/ServiceDemo/UserFeedback/styles.js | 8 +++----- .../UserProfile/UserProfileSettings/styles.js | 12 +++--------- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/components/Onboarding/TermsOfUse/styles.js b/src/components/Onboarding/TermsOfUse/styles.js index f2fee2018..29ce3dcfb 100644 --- a/src/components/Onboarding/TermsOfUse/styles.js +++ b/src/components/Onboarding/TermsOfUse/styles.js @@ -25,7 +25,7 @@ export const useStyles = (theme) => ({ }, }, termsAndConditions: { - height: 247, + height: "50vh", margin: "15px 12px 0", padding: "9px 7px", overflow: "auto", diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/styles.js b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/styles.js index dddd0d0b0..52c1d3eaa 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/styles.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/CompletedActions/styles.js @@ -3,11 +3,8 @@ import { makeStyles } from "@mui/styles"; export const useStyles = makeStyles((theme) => ({ buttonsContainer: { marginTop: 10, - textAlign: "center", - "& button": { - padding: "15px 60px", - marginTop: 10, - marginRight: "0 !important", - }, + display: "flex", + justifyContent: "center", + gap: 12, }, })); diff --git a/src/components/ServiceDetails/AboutService/ServiceDemo/UserFeedback/styles.js b/src/components/ServiceDetails/AboutService/ServiceDemo/UserFeedback/styles.js index d4eb5f2d2..dce3c9586 100644 --- a/src/components/ServiceDetails/AboutService/ServiceDemo/UserFeedback/styles.js +++ b/src/components/ServiceDetails/AboutService/ServiceDemo/UserFeedback/styles.js @@ -43,11 +43,9 @@ export const useStyles = makeStyles((theme) => ({ }, buttonsContainer: { marginTop: 10, - textAlign: "center", - "& button": { - padding: "15px 60px", - marginTop: 10, - }, + display: "flex", + justifyContent: "center", + gap: 12, }, InputWrapper: { "& span": { diff --git a/src/components/UserProfile/UserProfileSettings/styles.js b/src/components/UserProfile/UserProfileSettings/styles.js index 872d91d22..5cd53034e 100644 --- a/src/components/UserProfile/UserProfileSettings/styles.js +++ b/src/components/UserProfile/UserProfileSettings/styles.js @@ -27,18 +27,12 @@ export const useStyles = (theme) => ({ }, }, settingsContent: { + display: "flex", + flexDirection: "column", + gap: 24, padding: "30px 25px", "& div": { - width: 411, - margin: "30px 0 0", fontFamily: theme.typography.primary.main, - "@media(max-width:660px)": { - width: "100%", - flexDirection: "column", - }, - "&:first-of-type": { - marginTop: 0, - }, }, "& label": { color: theme.palette.text.black1, From 7cf636dd48483e450d064cecc9863e5729e88e9d Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:24:43 +0300 Subject: [PATCH 56/83] [SM-139] update user wallet tab style --- src/components/UserProfile/UserProfileAccount/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/UserProfile/UserProfileAccount/index.js b/src/components/UserProfile/UserProfileAccount/index.js index 5214de019..242725587 100644 --- a/src/components/UserProfile/UserProfileAccount/index.js +++ b/src/components/UserProfile/UserProfileAccount/index.js @@ -35,7 +35,7 @@ const UserProfileAccount = ({ classes }) => { const dispatch = useDispatch(); const parseWallet = (address, type) => { - return { value: address, label: `${type} (${address})`, address, type }; + return { value: address, label: address, address, type }; }; const getWallets = useCallback( @@ -136,7 +136,7 @@ const UserProfileAccount = ({ classes }) => { return ( - +

Payment / Transfer Method

@@ -149,7 +149,7 @@ const UserProfileAccount = ({ classes }) => {
{linkedProviders.length > 0 && ( - + )} From 07e2e8a54542688ba68f66553b8d6331825af543 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:25:30 +0300 Subject: [PATCH 57/83] [SM-139] update user delete form style --- .../ConfirmDelete/index.js | 55 +++++++--------- .../ConfirmDelete/styles.js | 66 +------------------ 2 files changed, 26 insertions(+), 95 deletions(-) diff --git a/src/components/UserProfile/UserProfileSettings/ConfirmDelete/index.js b/src/components/UserProfile/UserProfileSettings/ConfirmDelete/index.js index 904dc434d..b03bf12d3 100644 --- a/src/components/UserProfile/UserProfileSettings/ConfirmDelete/index.js +++ b/src/components/UserProfile/UserProfileSettings/ConfirmDelete/index.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import Modal from "@mui/material/Modal"; import Card from "@mui/material/Card"; import CardHeader from "@mui/material/CardHeader"; @@ -6,17 +6,13 @@ import CloseIcon from "@mui/icons-material/Close"; import IconButton from "@mui/material/IconButton"; import CardContent from "@mui/material/CardContent"; import CardActions from "@mui/material/CardActions"; -import OutlinedInput from "@mui/material/OutlinedInput"; -import InputLabel from "@mui/material/InputLabel"; -import MenuItem from "@mui/material/MenuItem"; -import FormControl from "@mui/material/FormControl"; -import Select from "@mui/material/Select"; import TextField from "@mui/material/TextField"; import { useStyles } from "./styles"; import StyledButton from "../../../common/StyledButton"; import BulletPoint from "../../../common/BulletPoint"; import AlertBox, { alertTypes } from "../../../common/AlertBox"; +import StyledDropdown from "@common/StyledDropdown"; const warningMessage = [ "Your wallet or any wallets you have used will remain in your possession. We do not have any access to your wallet and cannot help you recover wallet keys.", @@ -24,18 +20,18 @@ const warningMessage = [ "All personal data associated with your account will be deleted from our records.", ]; -const resonForLeaving = [ - { label: "I want to create another account", value: " " }, - { label: "I am dissatisfied with the platform", value: " " }, - { label: "There were not enough services", value: " " }, - { label: "It is too difficult to use", value: " " }, - { label: "It is too slow to use", value: " " }, - { label: "Other (Please describe below)", value: " " }, +const reasonsForLeaving = [ + { label: "I want to create another account", value: "0" }, + { label: "I am dissatisfied with the platform", value: "1" }, + { label: "There were not enough services", value: "2" }, + { label: "It is too difficult to use", value: "3" }, + { label: "It is too slow to use", value: "4" }, + { label: "Other (Please describe below)", value: "5" }, ]; const ConfirmDelete = ({ open, handleClose, handleSubmit, error }) => { const classes = useStyles(); - const [reasonForLeaving, setReasonForLeaving] = React.useState(""); + const [reasonForLeaving, setReasonForLeaving] = useState(); const handleCancel = () => { setReasonForLeaving(""); @@ -74,24 +70,19 @@ const ConfirmDelete = ({ open, handleClose, handleSubmit, error }) => {
-
- - Help us improve, tell us why you are leaving - - - -
+ + diff --git a/src/components/UserProfile/UserProfileSettings/ConfirmDelete/styles.js b/src/components/UserProfile/UserProfileSettings/ConfirmDelete/styles.js index 46b28ff5a..842e3762a 100644 --- a/src/components/UserProfile/UserProfileSettings/ConfirmDelete/styles.js +++ b/src/components/UserProfile/UserProfileSettings/ConfirmDelete/styles.js @@ -19,7 +19,9 @@ export const useStyles = makeStyles((theme) => ({ }, }, CardContent: { - padding: "16px 32px 0", + display: "flex", + flexDirection: "column", + gap: 24, "& h2": { color: theme.palette.text.darkShadedGray, fontSize: 18, @@ -35,36 +37,8 @@ export const useStyles = makeStyles((theme) => ({ }, }, CardActions: { - marginTop: 23, justifyContent: "center", }, - BeforeYouGoContent: { marginTop: 16 }, - DropDownContainer: { - borderWidth: 1, - borderStyle: "solid", - borderColor: "#828282", - borderRadius: 4, - position: "relative", - marginTop: 25, - padding: "11px 15px", - "& span": { - color: "#212121", - fontSize: 12, - lineHeight: "16px", - letterSpacing: 0.4, - position: "absolute", - top: -10, - left: 10, - background: "#fff", - display: "inline-block", - padding: "0 5px", - "& + div": { width: "100%" }, - }, - "& select": { - padding: "initial", - color: "#212121", - }, - }, WarningBoxConatiner: { borderWidth: 1, borderStyle: "solid", @@ -73,12 +47,6 @@ export const useStyles = makeStyles((theme) => ({ borderRadius: 4, marginTop: 16, padding: "13px 11px", - "& div": { - marginBottom: 17, - display: "flex", - alignItems: "flex-start", - "&:last-of-type": { marginBottom: 0 }, - }, "& p": { border: "none", margin: "0 0 12px", @@ -93,32 +61,4 @@ export const useStyles = makeStyles((theme) => ({ verticalAlign: "middle", }, }, - inputFieldContainer: { - marginTop: 35, - "& > div": { - width: "100%", - "& div": { - "&.MuiSelect-select": { - color: theme.palette.text.darkShadedGray, - padding: "23px 15px", - "&:focus": { - backgroundColor: "transparent", - }, - }, - }, - }, - "& div": { - "&.MuiTextField-root": { marginTop: 28 }, - }, - }, - menuItem: { - color: theme.palette.text.black1, - fontSize: 16, - letterSpacing: 0.5, - lineHeight: "28px", - "&:hover": { - color: theme.palette.text.primary, - backgroundColor: theme.palette.text.offWhiteColor, - }, - }, })); From 3126d0ea053a4164b973d0c34947bf8d14b151cf Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:27:24 +0300 Subject: [PATCH 58/83] [SM-139] hide user transactions history tab --- src/components/UserProfile/index.js | 16 ++++++++-------- src/utility/constants/UserPopupMenu.js | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/UserProfile/index.js b/src/components/UserProfile/index.js index 19bf785b1..20ab0572e 100644 --- a/src/components/UserProfile/index.js +++ b/src/components/UserProfile/index.js @@ -9,7 +9,7 @@ import { useNavigate, useLocation } from "react-router-dom"; import UserProfileSettings from "./UserProfileSettings"; import UserProfileHeader from "./UserProfileHeader"; import UserProfileAccount from "./UserProfileAccount"; -import UserProfileTransactionHistory from "./UserProfileTransactionHistory"; +// import UserProfileTransactionHistory from "./UserProfileTransactionHistory"; // import UserProfileModels from "./UserProfileModels"; import { useStyles } from "./styles"; import Routes from "../../utility/constants/Routes"; @@ -36,12 +36,12 @@ const UserProfile = ({ classes, nickname, email }) => { path: userProfileRoutes.SETTINGS, component: , }, - transactions: { - name: "Transactions", - index: 2, - path: userProfileRoutes.TRANSACTIONS, - component: , - }, + // transactions: { + // name: "PayPal Transactions", + // index: 2, + // path: userProfileRoutes.TRANSACTIONS, + // component: , + // }, }), [] ); @@ -50,7 +50,7 @@ const UserProfile = ({ classes, nickname, email }) => { () => ({ [`${tabs.account.path}`]: "account", [`${tabs.settings.path}`]: "settings", - [`${tabs.transactions.path}`]: "transactions", + // [`${tabs.transactions.path}`]: "transactions", }), [tabs] ); diff --git a/src/utility/constants/UserPopupMenu.js b/src/utility/constants/UserPopupMenu.js index 07549dd6b..2b8b7124f 100644 --- a/src/utility/constants/UserPopupMenu.js +++ b/src/utility/constants/UserPopupMenu.js @@ -18,11 +18,11 @@ export const UserMenuList = [ menuTitle: "Settings", menuLink: userProfileRoutes.SETTINGS, }, - { - menuIcon: SettingIcon, - menuTitle: "Transactions", - menuLink: userProfileRoutes.TRANSACTIONS, - }, + // { + // menuIcon: SettingIcon, + // menuTitle: "Transactions", + // menuLink: userProfileRoutes.TRANSACTIONS, + // }, ]; // if (process.env.REACT_APP_TRAINING_ENABLE === "true") { From 046796e9f8d72830ea135e5b69bc1d77818ff155 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:28:37 +0300 Subject: [PATCH 59/83] [SM-138] fix routing for not logged in users --- src/App.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index d1b79a494..51ac04884 100644 --- a/src/App.js +++ b/src/App.js @@ -51,16 +51,17 @@ initGDPRNotification(); const App = () => { const isLoggedIn = useSelector((state) => state.userReducer.login.isLoggedIn); - const isTermsAccepted = useSelector((state) => state.userReducer.isTermsAccepted); + const isTermsAccepted = useSelector((state) => state.userReducer.isTermsAccepted.accepted); const isInitialized = useSelector((state) => state.userReducer.isInitialized); const isLoggedInAndTermsAccepted = isLoggedIn && isTermsAccepted; - const isNotLoggedInPageAvailable = !isLoggedIn || isTermsAccepted; + const isNotLoggedInPageAvailable = !isLoggedIn || isLoggedInAndTermsAccepted; const dispatch = useDispatch(); useEffect(() => { dispatch(userActions.fetchUserDetails()); + dispatch(userActions.fetchAuthenticatedUser()); }, [dispatch]); const Loader = () => { From c9bf4fcf15ff6a4432ce58eab0de7300a51bf733 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:29:13 +0300 Subject: [PATCH 60/83] [SM-138] update dapp-user API --- src/config/APIEndpoints.js | 8 ++++---- src/utility/API.js | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/config/APIEndpoints.js b/src/config/APIEndpoints.js index 5199e3bfc..867641111 100644 --- a/src/config/APIEndpoints.js +++ b/src/config/APIEndpoints.js @@ -32,11 +32,11 @@ export const APIEndpoints = { }; export const APIPaths = { + USER: "/user", + UPDATE_USER_ALERTS: "/user/alerts", + FEEDBACK: "/user/review", + SIGNUP: "/signup", - GET_USER_PROFILE: "/profile", - UPDATE_USER_PROFILE: "/profile", - DELETE_USER: "/user/delete", - FEEDBACK: "/feedback", GET_SERVICE_LIST: "/service", FILTER_DATA: "/service?attribute=", SERVICE_DETAILS: (orgId, serviceId) => `/org/${orgId}/service/${serviceId}`, diff --git a/src/utility/API.js b/src/utility/API.js index 279122605..b180e8f47 100644 --- a/src/utility/API.js +++ b/src/utility/API.js @@ -1,4 +1,4 @@ -import { get, post } from "aws-amplify/api"; +import { get, post, del, put } from "aws-amplify/api"; export const initializeAPIOptions = (token, body, queryStringParameters) => { const options = { headers: { Authorization: token } }; @@ -11,6 +11,10 @@ export const initializeAPIOptions = (token, body, queryStringParameters) => { return options; }; +export const putAPI = (apiName, path, options) => { + return request(put({ apiName, path, options })); +}; + export const getAPI = (apiName, path, options) => { return request(get({ apiName, path, options })); }; @@ -19,6 +23,10 @@ export const postAPI = (apiName, path, options) => { return request(post({ apiName, path, options })); }; +export const deleteAPI = (apiName, path, options) => { + return request(del({ apiName, path, options })); +}; + const request = async (request) => { const { body } = await request.response; const response = await body.json(); From abf4d4e27dad60eaa40c85d652958fce1ffeebb4 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:30:30 +0300 Subject: [PATCH 61/83] [SM-138] update email alerts --- .../UserProfile/UserProfileSettings/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/UserProfile/UserProfileSettings/index.js b/src/components/UserProfile/UserProfileSettings/index.js index 1aea647dd..66caca45b 100644 --- a/src/components/UserProfile/UserProfileSettings/index.js +++ b/src/components/UserProfile/UserProfileSettings/index.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { withStyles } from "@mui/styles"; import Grid from "@mui/material/Grid"; import TextField from "@mui/material/TextField"; @@ -14,6 +14,7 @@ import AlertBox, { alertTypes } from "../../common/AlertBox"; import ConfirmDelete from "./ConfirmDelete"; import { Helmet } from "react-helmet"; import { useNavigate } from "react-router-dom"; +import { isUndefined } from "lodash"; const UserProfileSettings = ({ classes }) => { const navigate = useNavigate(); @@ -22,16 +23,18 @@ const UserProfileSettings = ({ classes }) => { const userEmail = useSelector((state) => state.userReducer.email); const nickname = useSelector((state) => state.userReducer.nickname); const emailAlerts = useSelector((state) => state.userReducer.emailAlerts); - const isTermsAccepted = useSelector((state) => state.userReducer.isTermsAccepted); - const [isEmailAlerts, setIsEmailAlerts] = useState(emailAlerts); + const [isEmailAlerts, setIsEmailAlerts] = useState(emailAlerts || false); const [alert, setAlert] = useState({ message: "", type: alertTypes.ERROR }); const [showConfirmDelete, setShowConfirmDelete] = useState(false); const [confirmDeleteError, setConfirmDeleteError] = useState(); - // const handleEmailChange = (event) => { - // setEmail(event.target.value); - // }; + useEffect(() => { + setIsEmailAlerts(emailAlerts); + if (isUndefined(emailAlerts)) { + dispatch(userActions.fetchUserAlerts()); + } + }, [dispatch, emailAlerts]); const handleEmailAlerts = () => { setIsEmailAlerts(!isEmailAlerts); @@ -47,9 +50,8 @@ const UserProfileSettings = ({ classes }) => { const handleSubmit = async () => { setAlert({}); - const updatedUserData = { email_alerts: isEmailAlerts, is_terms_accepted: isTermsAccepted }; try { - await dispatch(userActions.updateUserProfile(updatedUserData)); + await dispatch(userActions.updateUserProfile(isEmailAlerts)); setAlert({ type: alertTypes.SUCCESS, message: "Changes saved successfully" }); } catch (error) { setAlert({ type: alertTypes.ERROR, message: String(error) }); From 3655441a38434b5b3ba45e9c1d9ed867ed0a3bed Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:31:30 +0300 Subject: [PATCH 62/83] [SM-138] update reducers functions connected with dapp-user --- src/Redux/actionCreators/UserActions.js | 95 ++++++++++++------------- src/Redux/reducers/UserReducer.js | 4 +- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index b1bf22e7b..c9ea42104 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -13,7 +13,7 @@ import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; import { parseError } from "../../utility/ErrorHandling"; import { errorActions, loaderActions } from "./"; import { LoaderContent } from "../../utility/constants/LoaderContent"; -import { getAPI, initializeAPIOptions, postAPI } from "../../utility/API"; +import { getAPI, initializeAPIOptions, postAPI, deleteAPI, putAPI } from "../../utility/API"; import Routes from "../../utility/constants/Routes"; import { getSdk } from "./SDKActions"; @@ -51,18 +51,15 @@ const NEXT_SIGN_IN_STEP = { const setJWTExp = (exp) => ({ type: SET_JWT_EXP, payload: exp }); export const fetchAuthenticatedUser = () => async (dispatch) => { - // let bypassCache = false; - // const { exp } = getState().userReducer.jwt; - // const currentEpochInUTC = getCurrentUTCEpoch(); - // if (!exp || currentEpochInUTC >= Number(exp)) { - // bypassCache = true; - // } - - // const currentUser = await getCurrentUser(); //currentAuthenticatedUser({ bypassCache }); const { userAttributes, idToken } = await getCurrentUser(); const newExp = idToken.payload.exp; dispatch(setJWTExp(newExp)); + const publisherTnC = userAttributes["custom:publisher_tnc"] + ? JSON.parse(userAttributes["custom:publisher_tnc"]) + : { ver: "1", accepted: false }; + dispatch(updateIsTermsAccepted(publisherTnC)); + return { nickname: userAttributes.nickname, email: userAttributes.email, @@ -96,24 +93,18 @@ const updateIsTermsAccepted = (isTermsAccepted) => (dispatch) => { dispatch({ type: UPDATE_IS_TERMS_ACCEPTED, payload: isTermsAccepted }); }; -const fetchUserProfile = (token) => async (dispatch) => { +export const fetchUserAlerts = () => async (dispatch) => { + const { token } = await dispatch(fetchAuthenticatedUser()); const apiName = APIEndpoints.USER.name; - const path = APIPaths.GET_USER_PROFILE; + const path = APIPaths.USER; const apiOptions = initializeAPIOptions(token); try { const userProfile = await getAPI(apiName, path, apiOptions); - - if (userProfile.data.data.length === 0) { - dispatch(registerInMarketplace(token)); - return; - } - const userProfileData = userProfile.data.data[0]; - const isTermsAccepted = Boolean(userProfileData.is_terms_accepted); - const emailAlerts = Boolean(userProfileData.email_alerts); + const emailAlerts = Boolean(userProfile.data.emailAlerts); dispatch(updateEmailAlertsSubscription(emailAlerts)); - dispatch(updateIsTermsAccepted(isTermsAccepted)); } catch (err) { + console.error("Error fetching user alerts", err); return; } }; @@ -191,9 +182,7 @@ const fetchUserDetailsError = (err) => (dispatch) => { export const fetchUserDetails = () => async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.APP_INIT)); try { - const { nickname, token, email, email_verified } = await dispatch(fetchAuthenticatedUser()); - - await dispatch(fetchUserProfile(token)); + const { nickname, email, email_verified } = await dispatch(fetchAuthenticatedUser()); if (!email) { dispatch(noAuthenticatedUser()); return; @@ -208,13 +197,13 @@ export const fetchUserDetails = () => async (dispatch) => { export const updateUserProfileInit = (token, updatedUserData) => { const apiName = APIEndpoints.USER.name; - const path = APIPaths.UPDATE_USER_PROFILE; + const path = APIPaths.UPDATE_USER_ALERTS; const apiOptions = initializeAPIOptions(token, updatedUserData); - return postAPI(apiName, path, apiOptions); + return putAPI(apiName, path, apiOptions); }; -const updateUserProfileSuccess = (token) => (dispatch) => { - dispatch(fetchUserProfile(token)); +const updateUserProfileSuccess = () => (dispatch) => { + dispatch(fetchAuthenticatedUser()); dispatch(loaderActions.stopAppLoader()); }; @@ -223,17 +212,19 @@ const updateUserProfileFailure = (err) => (dispatch) => { dispatch(loaderActions.stopAppLoader()); }; -export const updateUserProfile = (updatedUserData) => async (dispatch) => { +export const updateUserProfile = (isEmailAlerts) => async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.UPDATE_PROFILE)); try { const { token } = await dispatch(fetchAuthenticatedUser()); - const response = await updateUserProfileInit(token, updatedUserData); + const response = await updateUserProfileInit(token, { emailAlerts: isEmailAlerts }); if (response.status === "success") { return dispatch(updateUserProfileSuccess(token)); } } catch (err) { dispatch(updateUserProfileFailure(err)); throw err; + } finally { + dispatch(loaderActions.stopAppLoader()); } }; @@ -260,9 +251,11 @@ const getCurrentUser = async () => { export const loginSuccess = ({ route }) => - async (dispatch, getState) => { - const { userAttributes, idToken } = await getCurrentUser(); - + async (dispatch) => { + const { userAttributes } = await getCurrentUser(); + const checkIsTermsAccepted = userAttributes?.["custom:publisher_tnc"] + ? JSON.parse(userAttributes?.["custom:publisher_tnc"]) + : { ver: "1", accepted: false }; const userDetails = { type: LOGIN_SUCCESS, payload: { @@ -270,13 +263,12 @@ export const loginSuccess = email: userAttributes.email, nickname: userAttributes.nickname, isEmailVerified: userAttributes.email_verified, + isTermsAccepted: checkIsTermsAccepted, }, }; dispatch(userDetails); - await dispatch(fetchUserProfile(idToken.toString())); - const isTermsAccepted = getState().userReducer.isTermsAccepted; - if (!isTermsAccepted) { + if (!checkIsTermsAccepted) { route = `/${Routes.ONBOARDING}`; } History.navigate(route); @@ -293,12 +285,14 @@ export const login = if (loginRequest?.isSignedIn) { dispatch(loginSuccess({ route })); + return; } if (loginRequest?.nextStep?.signInStep === NEXT_SIGN_IN_STEP.CONFIRM_SIGN_UP) { throw new Error("User does not exist."); } throw new Error("Something went wrong. Please, try again later"); } catch (err) { + console.error("login error", err); if (err?.code === "PasswordResetRequiredException") { dispatch(updateEmail(email)); History.navigate(`/${Routes.RESET_PASSWORD}`); @@ -327,19 +321,19 @@ export const login = } }; -const registrationAPI = (token) => { - const apiName = APIEndpoints.USER.name; - const apiPath = APIPaths.SIGNUP; - const apiOptions = initializeAPIOptions(token); - return getAPI(apiName, apiPath, apiOptions); -}; +// const registrationAPI = (token) => { +// const apiName = APIEndpoints.USER.name; +// const apiPath = APIPaths.SIGNUP; +// const apiOptions = initializeAPIOptions(token); +// return getAPI(apiName, apiPath, apiOptions); +// }; -const registerInMarketplace = (token) => async (dispatch) => { - const response = await registrationAPI(token); - if (response.data === "success") { - dispatch(fetchUserProfile(token)); - } -}; +// const registerInMarketplace = (token) => async (dispatch) => { +// const response = await registrationAPI(token); +// if (response.data === "success") { +// dispatch(fetchAuthenticatedUser()); +// } +// }; export const signOut = () => (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.SIGN_OUT)); @@ -387,9 +381,9 @@ const userDeleted = (route) => (dispatch) => { const deleteUserFromMarketPlace = (token) => { const apiName = APIEndpoints.USER.name; - const path = APIPaths.DELETE_USER; + const path = APIPaths.USER; const apiOptions = initializeAPIOptions(token); - return getAPI(apiName, path, apiOptions); + return deleteAPI(apiName, path, apiOptions); }; const deleteUserFromCognito = (route) => (dispatch) => { @@ -407,9 +401,10 @@ export const deleteUserAccount = (route) => async (dispatch) => { const currentUser = await getCurrentUser(); await deleteUserFromMarketPlace(currentUser.idToken.toString()); dispatch(deleteUserFromCognito(route)); - dispatch(loaderActions.stopAppLoader()); } catch (error) { console.log(error); + } finally { + dispatch(loaderActions.stopAppLoader()); } }; diff --git a/src/Redux/reducers/UserReducer.js b/src/Redux/reducers/UserReducer.js index 9490e0d30..bee5ed206 100644 --- a/src/Redux/reducers/UserReducer.js +++ b/src/Redux/reducers/UserReducer.js @@ -18,8 +18,8 @@ const InitialUserDetails = { firstTimeFetchWallet: true, email: "", nickname: "", - emailAlerts: false, - isTermsAccepted: false, + emailAlerts: undefined, + isTermsAccepted: { ver: "0", accepted: false }, transactionHistory: [], jwt: { exp: "", From f7f4f4b1379c32332539580634d50ae7df2425ff Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:32:05 +0300 Subject: [PATCH 63/83] [SM-138] update terms of use component --- src/components/Onboarding/TermsOfUse/index.js | 23 +++++++++++----- src/components/Onboarding/index.js | 27 ++++--------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/components/Onboarding/TermsOfUse/index.js b/src/components/Onboarding/TermsOfUse/index.js index c2677fb2d..c03877239 100644 --- a/src/components/Onboarding/TermsOfUse/index.js +++ b/src/components/Onboarding/TermsOfUse/index.js @@ -1,40 +1,51 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { withStyles } from "@mui/styles"; import FormControlLabel from "@mui/material/FormControlLabel"; import Checkbox from "@mui/material/Checkbox"; -import { useDispatch } from "react-redux"; import StyledButton from "../../common/StyledButton"; import { useStyles } from "./styles"; import PrivacyTerms from "./PrivacyTerms"; -import { userActions } from "../../../Redux/actionCreators"; +import { updateUserAttributes } from "aws-amplify/auth"; import Routes from "../../../utility/constants/Routes"; import AlertBox, { alertTypes } from "../../common/AlertBox"; import { useLocation, useNavigate } from "react-router-dom"; +import { useSelector } from "react-redux"; const TermsOfUse = ({ classes }) => { const navigate = useNavigate(); const location = useLocation(); - const dispatch = useDispatch(); + const isTermsAcceptedPrev = useSelector((state) => state.userReducer.isTermsAccepted.accepted); const [isTermsAccepted, setIsTermsAccepted] = useState(false); const [alertMessage, setAlertMessage] = useState(); + useEffect(() => { + if (isTermsAcceptedPrev) { + const newLocation = + location?.state && location?.state?.sourcePath ? location.state.sourcePath : `/${Routes.AI_MARKETPLACE}`; + navigate(newLocation, { replace: true }); + } + }, [navigate, isTermsAcceptedPrev]); + const handleChange = (event) => { setIsTermsAccepted(event.target.checked); }; const handleSubmit = async () => { setAlertMessage(); - const updatedUserData = { is_terms_accepted: isTermsAccepted, email_alerts: false }; + const tncValue = { ver: "1", accepted: isTermsAccepted }; + const userAttributes = { "custom:publisher_tnc": JSON.stringify(tncValue) }; try { - await dispatch(userActions.updateUserProfile(updatedUserData)); + await updateUserAttributes({ userAttributes }); + if (location.state && location.state.sourcePath) { navigate(location.state.sourcePath); return; } navigate(`/${Routes.AI_MARKETPLACE}`); } catch (error) { + console.error("Error updating user attributes:", error); if (error.response && error.response.data && error.response.data.error) { setAlertMessage(error.response.data.error); return; diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 5b70eb9d4..b25f1d6ec 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { useSelector } from "react-redux"; import { Helmet } from "react-helmet"; import { withStyles } from "@mui/styles"; @@ -7,38 +7,21 @@ import Authentication from "./Authentication"; import TermsOfUse from "./TermsOfUse"; import { useStyles } from "./styles"; import OnboardingContainer from "./OnboardingContainer"; -import Routes from "../../utility/constants/Routes"; const Onboarding = ({ classes }) => { const navigate = useNavigate(); - const location = useLocation(); const isEmailVerified = useSelector((state) => state.userReducer.isEmailVerified); - const isTermsAccepted = useSelector((state) => state.userReducer.isTermsAccepted); const nickname = useSelector((state) => state.userReducer.nickname); const [activeSection, setActiveSection] = useState(0); const progressText = [{ label: "Authentication" }, { label: "Terms of service" }]; useEffect(() => { - const initialChecks = () => { - if (!isEmailVerified) { - return; - } - if (activeSection === 0) { - setActiveSection(1); - } - if (isTermsAccepted) { - if (location?.state && location?.state?.sourcePath) { - navigate(location.state.sourcePath); - return; - } - navigate(`/${Routes.AI_MARKETPLACE}`); - } - }; - - initialChecks(); - }, [navigate, isEmailVerified, isTermsAccepted, activeSection, location.state]); + if (isEmailVerified) { + setActiveSection(1); + } + }, [navigate, isEmailVerified]); const handleNextSection = () => { setActiveSection(activeSection + 1); From 48c1c2c071b1832f05d6e737e8004d7f8aaff557 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Mon, 30 Jun 2025 16:33:07 +0300 Subject: [PATCH 64/83] [SM-138] update feedback payload --- src/Redux/actionCreators/ServiceActions.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index bb56945ac..e5a049693 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -125,12 +125,10 @@ const submitFeedbackAPI = (feedbackObj, token) => { export const submitFeedback = (orgId, serviceId, feedback) => async (dispatch) => { const { token } = await dispatch(userActions.fetchAuthenticatedUser()); const feedbackObj = { - feedback: { - org_id: orgId, - service_id: serviceId, - user_rating: parseFloat(feedback.rating).toFixed(1), - comment: feedback.comment, - }, + orgId, + serviceId, + userRating: Number(parseFloat(feedback.rating).toFixed(1)), + comment: feedback.comment, }; return submitFeedbackAPI(feedbackObj, token); }; From 77a6993517f86a8c264f42eb2f27aff15d7c2f3d Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Tue, 1 Jul 2025 13:34:26 +0300 Subject: [PATCH 65/83] [SM-138] update loading of info about accepting terms --- src/App.js | 2 +- src/Redux/actionCreators/UserActions.js | 14 +++++++++----- src/components/Onboarding/TermsOfUse/index.js | 5 ++++- src/components/Onboarding/TermsOfUse/styles.js | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/App.js b/src/App.js index 51ac04884..7617cb87d 100644 --- a/src/App.js +++ b/src/App.js @@ -61,7 +61,7 @@ const App = () => { useEffect(() => { dispatch(userActions.fetchUserDetails()); - dispatch(userActions.fetchAuthenticatedUser()); + dispatch(userActions.getIsTermsAcceptedInfo()); }, [dispatch]); const Loader = () => { diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index c9ea42104..0c244052f 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -5,6 +5,7 @@ import { resetPassword, confirmResetPassword, deleteUser, + fetchUserAttributes, } from "aws-amplify/auth"; import isEmpty from "lodash/isEmpty"; import moment from "moment"; @@ -55,11 +56,6 @@ export const fetchAuthenticatedUser = () => async (dispatch) => { const newExp = idToken.payload.exp; dispatch(setJWTExp(newExp)); - const publisherTnC = userAttributes["custom:publisher_tnc"] - ? JSON.parse(userAttributes["custom:publisher_tnc"]) - : { ver: "1", accepted: false }; - dispatch(updateIsTermsAccepted(publisherTnC)); - return { nickname: userAttributes.nickname, email: userAttributes.email, @@ -211,6 +207,14 @@ const updateUserProfileFailure = (err) => (dispatch) => { dispatch(errorActions.updateProfileSettingsError(String(err))); dispatch(loaderActions.stopAppLoader()); }; +export const getIsTermsAcceptedInfo = () => async (dispatch) => { + const newAttributes = await fetchUserAttributes(); + const checkIsTermsAccepted = newAttributes?.["custom:publisher_tnc"] + ? JSON.parse(newAttributes?.["custom:publisher_tnc"]) + : { ver: "1", accepted: false }; + + dispatch(updateIsTermsAccepted(checkIsTermsAccepted)); +}; export const updateUserProfile = (isEmailAlerts) => async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.UPDATE_PROFILE)); diff --git a/src/components/Onboarding/TermsOfUse/index.js b/src/components/Onboarding/TermsOfUse/index.js index c03877239..0571fe11b 100644 --- a/src/components/Onboarding/TermsOfUse/index.js +++ b/src/components/Onboarding/TermsOfUse/index.js @@ -10,11 +10,13 @@ import { updateUserAttributes } from "aws-amplify/auth"; import Routes from "../../../utility/constants/Routes"; import AlertBox, { alertTypes } from "../../common/AlertBox"; import { useLocation, useNavigate } from "react-router-dom"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import { getIsTermsAcceptedInfo } from "../../../Redux/actionCreators/UserActions"; const TermsOfUse = ({ classes }) => { const navigate = useNavigate(); const location = useLocation(); + const dispatch = useDispatch(); const isTermsAcceptedPrev = useSelector((state) => state.userReducer.isTermsAccepted.accepted); const [isTermsAccepted, setIsTermsAccepted] = useState(false); @@ -38,6 +40,7 @@ const TermsOfUse = ({ classes }) => { const userAttributes = { "custom:publisher_tnc": JSON.stringify(tncValue) }; try { await updateUserAttributes({ userAttributes }); + dispatch(getIsTermsAcceptedInfo()); if (location.state && location.state.sourcePath) { navigate(location.state.sourcePath); diff --git a/src/components/Onboarding/TermsOfUse/styles.js b/src/components/Onboarding/TermsOfUse/styles.js index 29ce3dcfb..5f5eaa44e 100644 --- a/src/components/Onboarding/TermsOfUse/styles.js +++ b/src/components/Onboarding/TermsOfUse/styles.js @@ -25,7 +25,7 @@ export const useStyles = (theme) => ({ }, }, termsAndConditions: { - height: "50vh", + height: 250, margin: "15px 12px 0", padding: "9px 7px", overflow: "auto", From abb0b4b7ef47bb839b90cf6e58806287f37603c2 Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Wed, 2 Jul 2025 15:52:22 +0300 Subject: [PATCH 66/83] [SM-138] fix loading of info about accepting terms and email alerts --- src/Redux/reducers/UserReducer.js | 2 +- .../UserProfile/UserProfileSettings/index.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Redux/reducers/UserReducer.js b/src/Redux/reducers/UserReducer.js index bee5ed206..d72223849 100644 --- a/src/Redux/reducers/UserReducer.js +++ b/src/Redux/reducers/UserReducer.js @@ -19,7 +19,7 @@ const InitialUserDetails = { email: "", nickname: "", emailAlerts: undefined, - isTermsAccepted: { ver: "0", accepted: false }, + isTermsAccepted: { ver: "0", accepted: true }, transactionHistory: [], jwt: { exp: "", diff --git a/src/components/UserProfile/UserProfileSettings/index.js b/src/components/UserProfile/UserProfileSettings/index.js index 66caca45b..b7847df1f 100644 --- a/src/components/UserProfile/UserProfileSettings/index.js +++ b/src/components/UserProfile/UserProfileSettings/index.js @@ -5,6 +5,7 @@ import TextField from "@mui/material/TextField"; import FormControlLabel from "@mui/material/FormControlLabel"; import Checkbox from "@mui/material/Checkbox"; import { useDispatch, useSelector } from "react-redux"; +import PropTypes from "prop-types"; import StyledButton from "../../common/StyledButton"; import { useStyles } from "./styles"; @@ -14,7 +15,6 @@ import AlertBox, { alertTypes } from "../../common/AlertBox"; import ConfirmDelete from "./ConfirmDelete"; import { Helmet } from "react-helmet"; import { useNavigate } from "react-router-dom"; -import { isUndefined } from "lodash"; const UserProfileSettings = ({ classes }) => { const navigate = useNavigate(); @@ -31,9 +31,7 @@ const UserProfileSettings = ({ classes }) => { useEffect(() => { setIsEmailAlerts(emailAlerts); - if (isUndefined(emailAlerts)) { - dispatch(userActions.fetchUserAlerts()); - } + dispatch(userActions.fetchUserAlerts()); }, [dispatch, emailAlerts]); const handleEmailAlerts = () => { @@ -152,3 +150,7 @@ const UserProfileSettings = ({ classes }) => { }; export default withStyles(useStyles)(UserProfileSettings); + +UserProfileSettings.propTypes = { + classes: PropTypes.object.isRequired, +}; From bd4e7da2ef7e01e8965a8d1dac3aae2d20b5ee5d Mon Sep 17 00:00:00 2001 From: Marina Fedyantseva Date: Fri, 4 Jul 2025 18:47:48 +0300 Subject: [PATCH 67/83] [SM-137] update contract api filters --- scripts/sitemap-generator.js | 22 ++-- src/Redux/actionCreators/ServiceActions.js | 49 ++++---- src/Redux/reducers/ServiceReducer.js | 8 +- .../MainSection/Filter/ServiceSortOptions.js | 41 ------ .../AiMarketplace/MainSection/Filter/index.js | 118 +++++++++++++----- .../MainSection/Filter/styles.js | 10 -- .../MainSection/StyledPagination/index.js | 42 +++++-- .../AiMarketplace/MainSection/index.js | 20 +-- src/sandbox/sandbox_state.js | 10 +- src/utility/constants/Pagination.js | 57 +++------ 10 files changed, 185 insertions(+), 192 deletions(-) delete mode 100644 src/components/AiMarketplace/MainSection/Filter/ServiceSortOptions.js diff --git a/scripts/sitemap-generator.js b/scripts/sitemap-generator.js index 699560de8..134999c72 100644 --- a/scripts/sitemap-generator.js +++ b/scripts/sitemap-generator.js @@ -7,11 +7,11 @@ require("@babel/register")({ const defaultPagination = { q: "", limit: 50, - offset: 0, - total_count: 0, + page: 1, + totalCount: 0, s: "all", - sort_by: "ranking", - order_by: "asc", + sort: "ranking", + order: "asc", filters: [], }; @@ -24,17 +24,17 @@ async function fetchServices(pagination = defaultPagination, step = 50) { data: { data }, } = await axios.post(url, pagination); const { result } = data; - console.log("offset", data.offset); + console.log("page", data.page); console.log("limit", data.limit); console.log("services length", services.length); - console.log("total count", data.total_count); + console.log("total count", data.totalCount); console.log("-----****-----"); services = services.concat(result); - if (services.length < data.total_count) { + if (services.length < data.totalCount) { const enhancedPagination = Object.assign({}, pagination, { - offset: pagination.offset + step, + page: pagination.page + step, }); - if (enhancedPagination.offset > data.total_count) { + if (enhancedPagination.page > data.totalCount) { throw new Error("Trying to fetch more than total count"); } await fetchServices(enhancedPagination); @@ -55,8 +55,8 @@ async function generateSitemap() { await fetchServices(); console.log("fetched all services"); const idMap = services.map((service) => ({ - orgId: service.org_id, - serviceId: service.service_id, + orgId: service.orgId, + serviceId: service.serviceId, })); const activeUserTabsMap = ["account", "settings", "transactions"].map((tab) => ({ "activeTab?": tab })); diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index e5a049693..eeb1f73be 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -1,8 +1,7 @@ import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; import { loaderActions, userActions } from "./"; import { getAPI, postAPI, initializeAPIOptions } from "../../utility/API"; -import { generateOrganizationsFilterObject } from "../../utility/constants/Pagination"; -// import { cacheS3Url } from "../../utility/image"; +import { defaultActiveFilterItem } from "../../utility/constants/Pagination"; export const UPDATE_SERVICE_LIST = "SET_SERVICE_LIST"; export const UPDATE_PAGINATION_DETAILS = "SET_PAGINATION_DETAILS"; @@ -13,18 +12,23 @@ export const UPDATE_FILTER_DATA = "UPDATE_FILTER_DATA"; export const UPDATE_ACTIVE_FILTER_ITEM = "UPDATE_ACTIVE_FILTER_ITEM"; export const RESET_FILTER_ITEM = "RESET_FILTER_ITEM"; export const UPDATE_FEEDBACK = "UPDATE_FEEDBACK"; +export const UPDATE_SRVICE_COUNT = "UPDATE_SRVICE_COUNT"; export const updateActiveFilterItem = (activeFilterItem) => (dispatch) => { dispatch({ type: UPDATE_ACTIVE_FILTER_ITEM, payload: { ...activeFilterItem } }); }; +const updateServicesTotalCount = (totalCount) => (dispatch) => { + dispatch({ type: UPDATE_SRVICE_COUNT, payload: totalCount }); +}; + export const resetFilterItem = (dispatch) => { dispatch({ type: RESET_FILTER_ITEM }); }; export const fetchServiceSuccess = (res) => (dispatch) => { - dispatch(updatePagination({ total_count: res.data.total_count })); - dispatch({ type: UPDATE_SERVICE_LIST, payload: res.data.result }); + dispatch(updateServicesTotalCount(res.totalCount)); + dispatch({ type: UPDATE_SERVICE_LIST, payload: res.services }); dispatch(loaderActions.stopAIServiceListLoader()); }; @@ -38,29 +42,26 @@ export const fetchUserOrganizationsList = () => async (dispatch) => { const onlyUserOrgsFilter = () => async (dispatch) => { const userOrganizations = await dispatch(fetchUserOrganizationsList()); - const userOrganizationsId = userOrganizations.data.map((organization) => organization.org_id); - const filterObj = generateOrganizationsFilterObject([ - ...userOrganizationsId, - process.env.REACT_APP_EXAMPLE_SERVICE_ORG_ID, - ]); + const userOrganizationsId = userOrganizations.data.map((organization) => organization.orgId); + const filterObj = { orgId: [...userOrganizationsId, process.env.REACT_APP_EXAMPLE_SERVICE_ORG_ID] }; return filterObj; }; export const fetchService = - (pagination, filters = []) => + (pagination, filter = defaultActiveFilterItem) => async (dispatch) => { // env variable is string if (process.env.REACT_APP_IS_ALL_SERVICES_AVAILIBLE !== "true") { - filters = await dispatch(onlyUserOrgsFilter()); + filter = await dispatch(onlyUserOrgsFilter()); } dispatch(loaderActions.startAIServiceListLoader()); const url = new URL(APIEndpoints.CONTRACT.endpoint + APIPaths.GET_SERVICE_LIST); return fetch(url, { method: "POST", - body: JSON.stringify({ ...pagination, filters }), + body: JSON.stringify({ ...pagination, filter }), }) .then((res) => res.json()) - .then((res) => dispatch(fetchServiceSuccess(res))) + .then((res) => dispatch(fetchServiceSuccess(res.data))) .catch(() => dispatch(loaderActions.stopAIServiceListLoader())); }; @@ -80,18 +81,14 @@ export const fetchFilterData = (attribute) => (dispatch) => { }); }; -export const handleFilterChange = - ({ pagination, filterObj, currentActiveFilterData }) => - (dispatch) => { - dispatch(loaderActions.startAIServiceListLoader()); - Promise.all([ - dispatch(updatePagination(pagination)), - dispatch(fetchService(pagination, filterObj)), - dispatch(updateActiveFilterItem(currentActiveFilterData)), - ]) - .then(() => dispatch(loaderActions.stopAIServiceListLoader())) - .catch(() => dispatch(loaderActions.stopAIServiceListLoader())); - }; +export const handleFilterChange = (pagination, filter) => async (dispatch) => { + dispatch(loaderActions.startAIServiceListLoader()); + dispatch(updatePagination(pagination)); + dispatch(updateActiveFilterItem(filter)); + + await dispatch(fetchService(pagination, filter)); + dispatch(loaderActions.stopAIServiceListLoader()); +}; export const resetFilter = ({ pagination }) => @@ -104,7 +101,7 @@ export const resetFilter = const fetchFeedbackAPI = (orgId, serviceId, token) => { const apiName = APIEndpoints.USER.name; - const path = `${APIPaths.FEEDBACK}?org_id=${orgId}&service_id=${serviceId}`; + const path = `${APIPaths.FEEDBACK}?orgId=${orgId}&serviceId=${serviceId}`; const apiOptions = initializeAPIOptions(token); return getAPI(apiName, path, apiOptions); }; diff --git a/src/Redux/reducers/ServiceReducer.js b/src/Redux/reducers/ServiceReducer.js index f23076aa7..0887b28b4 100644 --- a/src/Redux/reducers/ServiceReducer.js +++ b/src/Redux/reducers/ServiceReducer.js @@ -4,8 +4,11 @@ import { defaultListingConfig, defaultActiveFilterItem } from "../../utility/con const InitialServiceList = { services: [], pagination: { ...defaultListingConfig }, + totalCount: 0, filterData: { - org_id: [], + orgId: [], + tagName: [], + onlyAvailable: true, }, activeFilterItem: { ...defaultActiveFilterItem }, serviceMethodExecution: { @@ -19,6 +22,9 @@ const serviceReducer = (state = InitialServiceList, action) => { case serviceActions.UPDATE_PAGINATION_DETAILS: { return { ...state, pagination: { ...state.pagination, ...action.payload } }; } + case serviceActions.UPDATE_SRVICE_COUNT: { + return { ...state, totalCount: action.payload }; + } case serviceActions.UPDATE_SERVICE_LIST: { return { ...state, services: action.payload }; } diff --git a/src/components/AiMarketplace/MainSection/Filter/ServiceSortOptions.js b/src/components/AiMarketplace/MainSection/Filter/ServiceSortOptions.js deleted file mode 100644 index 22cd1adae..000000000 --- a/src/components/AiMarketplace/MainSection/Filter/ServiceSortOptions.js +++ /dev/null @@ -1,41 +0,0 @@ -import React, { Fragment, useState } from "react"; -import { connect } from "react-redux"; - -import StyledDropdown from "../../../common/StyledDropdown"; -import { useStyles } from "./styles"; -import { sortByCategories, defaultPaginationParameters } from "../../../../utility/constants/Pagination"; -import { serviceActions } from "../../../../Redux/actionCreators"; - -const ServiceSortOptions = ({ pagination, updatePagination, fetchService }) => { - const [activeSortItem, setActiveSortItem] = useState("default"); - const classes = useStyles(); - - const handleSortChange = async (event) => { - const value = event.target.value; - if (value === "default" || value === activeSortItem) { - return; - } - const latestPagination = { ...pagination, ...defaultPaginationParameters, sort_by: value, order_by: "asc" }; - updatePagination(latestPagination); - await fetchService(latestPagination); - setActiveSortItem(value); - }; - - return ( - - Sort by: - - - ); -}; - -const mapStateToProps = (state) => ({ - pagination: state.serviceReducer.pagination, -}); - -const mapDispatchToProps = (dispatch) => ({ - fetchService: (pagination) => dispatch(serviceActions.fetchService(pagination)), - updatePagination: (pagination) => dispatch(serviceActions.updatePagination(pagination)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(ServiceSortOptions); diff --git a/src/components/AiMarketplace/MainSection/Filter/index.js b/src/components/AiMarketplace/MainSection/Filter/index.js index 286185071..f7c25ec09 100644 --- a/src/components/AiMarketplace/MainSection/Filter/index.js +++ b/src/components/AiMarketplace/MainSection/Filter/index.js @@ -1,23 +1,31 @@ import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import Grid from "@mui/material/Grid"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Checkbox from "@mui/material/Checkbox"; import { useStyles } from "./styles"; import SearchInputToggler from "./SearchInputToggler"; -import ServiceSortOptions from "./ServiceSortOptions"; import ViewToggler from "./ViewToggler"; import StyledDropdown from "@common/StyledDropdown"; import { serviceActions } from "@redux/actionCreators"; -import { defaultPaginationParameters, generateOrganizationsFilterObject } from "@utility/constants/Pagination"; +import { defaultPaginationParameters } from "@utility/constants/Pagination"; +import { sortByCategories } from "../../../../utility/constants/Pagination"; -const Filter = ({ listView, total_count, handleSearchChange, toggleView, currentPagination, showToggler }) => { - const { filterData, pagination } = useSelector((state) => state.serviceReducer); +const Filter = ({ listView, handleSearchChange, toggleView, currentPagination, showToggler }) => { + const classes = useStyles(); + const dispatch = useDispatch(); + const { totalCount, filterData, pagination, activeFilterItem } = useSelector((state) => state.serviceReducer); + const activeFilterDefault = { + orgId: activeFilterItem?.orgId || "default", + tagName: activeFilterItem?.tagName || "default", + }; const [showSearchInput, toggleSearchInput] = useState(false); const [searchKeyword, setSearchKeyword] = useState(""); - const [activeOrgItem, setActiveOrgItem] = useState("default"); - - const dispatch = useDispatch(); + const [activeFilter, setActiveFilter] = useState(activeFilterDefault); + const [activeSortItem, setActiveSortItem] = useState("default"); + const [isViewOnlyAvailable, setIsViewOnlyAvailable] = useState(activeFilterItem.onlyAvailable); useEffect(() => { return () => dispatch(serviceActions.resetFilter({ pagination })); @@ -27,57 +35,109 @@ const Filter = ({ listView, total_count, handleSearchChange, toggleView, current const handleSearch = (event) => { setSearchKeyword(event.currentTarget.value); const pagination = { - offset: 0, + page: 1, q: event.target.value, }; handleSearchChange({ ...currentPagination, ...pagination }); }; - const enhancedFilterData = filterData.org_id.map((el) => ({ - value: el.key, - label: el.value, + const enhancedorgIdFilterData = Object.keys(filterData.orgId).map((key) => ({ + value: key, + label: filterData.orgId[key], + })); + + const enhancedTagFilterData = filterData.tagName.map((tag) => ({ + value: tag, + label: tag, })); - const handleOrgFilterChange = (event) => { + const handleOrgFilterChange = async (event) => { const { value, name } = event.target; - if (value === activeOrgItem) { + + if (value === activeFilter[name]) { return; } - let filterObj = []; - let currentActiveFilterData = {}; - if (value !== "default") { - filterObj = generateOrganizationsFilterObject([value]); - currentActiveFilterData = { [name]: [value] }; + + const filterObj = { ...activeFilterItem, [name]: [value] }; + + if (value === "default") { + setActiveFilter({ ...activeFilter, [name]: "default" }); + delete filterObj[name]; + } else { + setActiveFilter({ ...activeFilter, [name]: [value] }); } - setActiveOrgItem(value); - const latestPagination = { ...pagination, ...defaultPaginationParameters, q: pagination.q }; - dispatch(serviceActions.handleFilterChange({ pagination: latestPagination, filterObj, currentActiveFilterData })); + const latestPagination = { ...pagination, page: defaultPaginationParameters.page }; + await dispatch(serviceActions.handleFilterChange(latestPagination, filterObj)); }; - const classes = useStyles(); + const handleSortChange = async (event) => { + const value = event.target.value; + if (value === "default" || value === activeSortItem) { + return; + } + setActiveSortItem(value); + const latestPagination = { ...pagination, page: defaultPaginationParameters.page, sort: value }; + await dispatch(serviceActions.handleFilterChange(latestPagination, activeFilterItem)); + }; + + const handleOnlyAvailableChange = async () => { + setIsViewOnlyAvailable(!isViewOnlyAvailable); + console.log("pagination: ", pagination); + + await dispatch( + serviceActions.handleFilterChange( + { ...pagination, page: defaultPaginationParameters.page }, + { ...activeFilterItem, onlyAvailable: !isViewOnlyAvailable } + ) + ); + }; return ( - +
- + Sort by +
{process.env.REACT_APP_IS_ALL_SERVICES_AVAILIBLE === "true" && (
Organization
)} +
+ Tag + +
+
+ } + label="Only available" + labelPlacement="start" + /> +
- - {total_count} services + + {totalCount} services