diff --git a/.env.example b/.env.example index 05b433818..a91d4aca9 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,3 @@ -# Default network, possible values: -# "development", "ropsten", "rinkeby", "mainnet", "polygon", "moonbeamalpha", -# "gaiaxtestnet", "mumbai", "bsc" -GATSBY_NETWORK="rinkeby" #GATSBY_INFURA_PROJECT_ID="xxx" #GATSBY_MARKET_FEE_ADDRESS="0xxx" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ca5b94331..bf7e5ff79 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,6 @@ jobs: - run: npm run build env: - GATSBY_NETWORK: ${{ secrets.GATSBY_NETWORK }} GATSBY_INFURA_PROJECT_ID: ${{ secrets.GATSBY_INFURA_PROJECT_ID }} GATSBY_PORTIS_ID: ${{ secrets.GATSBY_PORTIS_ID }} diff --git a/.gitignore b/.gitignore index 27ca46bfc..0735b891b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ public/storybook .vercel repo-metadata.json content/networks-metadata.json -src/@types/apollo \ No newline at end of file +src/@types/apollo +graphql.schema.json +src/@types/graph.types.ts diff --git a/README.md b/README.md index 4ffc0f81f..e9b648710 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ ## 🏄 Get Started -The app is a React app built with [Gatsby.js](https://www.gatsbyjs.org) + TypeScript + CSS modules and will connect to Ocean components in Rinkeby by default. +The app is a React app built with [Gatsby.js](https://www.gatsbyjs.org) + TypeScript + CSS modules and will connect to Ocean remote components by default. To start local development: @@ -73,7 +73,7 @@ Barge will deploy contracts to the local Ganache node which will take some time. Finally, set environment variables to use this local connection in `.env` in the app: ```bash -# modify env variables, setting GATSBY_NETWORK="development" +# modify env variables cp .env.example .env npm start @@ -111,7 +111,7 @@ All this data then comes from multiple sources: ### Aquarius -All initial data sets and their metadata (DDO) is retrieved client-side on run-time from the [Aquarius](https://github.com/oceanprotocol/aquarius) instance for each network. All app calls to Aquarius are done with 2 internal methods which mimic the same methods in ocean.js, but allow us: +All initial data sets and their metadata (DDO) is retrieved client-side on run-time from the [Aquarius](https://github.com/oceanprotocol/aquarius) instance, defined in `app.config.js`. All app calls to Aquarius are done with 2 internal methods which mimic the same methods in ocean.js, but allow us: - to cancel requests when components get unmounted in combination with [axios](https://github.com/axios/axios) - hit Aquarius as early as possible without relying on any ocean.js initialization @@ -127,25 +127,22 @@ const queryLatest = { offset: 9, query: { // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html + query_string: { query: `-isInPurgatory:true` } }, sort: { created: -1 } } function Component() { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const [result, setResult] = useState() useEffect(() => { - if (!config?.metadataCacheUri) return + if (!appConfig.metadataCacheUri) return const source = axios.CancelToken.source() async function init() { - const result = await queryMetadata( - query, - config.metadataCacheUri, - source.token - ) + const result = await queryMetadata(query, source.token) setResult(result) } init() @@ -153,7 +150,7 @@ function Component() { return () => { source.cancel() } - }, [config?.metadataCacheUri, query]) + }, [appConfig.metadataCacheUri, query]) return
{result}
} @@ -174,10 +171,10 @@ function Component() { Most financial data in the market is retrieved with GraphQL from [our own subgraph](https://github.com/oceanprotocol/ocean-subgraph), rendered on top of the initial data coming from Aquarius. -The app has [Apollo Client](https://www.apollographql.com/docs/react/) setup to query the respective subgraph based on network. In any component this client can be used like so: +The app has [Urql Client](https://formidable.com/open-source/urql/docs/basics/react-preact/) setup to query the respective subgraph based on network. In any component this client can be used like so: ```tsx -import { gql, useQuery } from '@apollo/client' +import { gql, useQuery } from 'urql' const query = gql` query PoolLiquidity($id: ID!, $shareId: ID) { @@ -266,10 +263,14 @@ Within components this metadata can be queried for under `allNetworksMetadataJso ```tsx export default function NetworkName(): ReactElement { - const { networkDisplayName, isTestnet } = useWeb3() + const { networkId, isTestnet } = useWeb3() + const { networksList } = useNetworkMetadata() + const networkData = getNetworkDataById(networksList, networkId) + const networkName = getNetworkDisplayName(networkData, networkId) + return ( <> - {networkDisplayName} {isTestnet && `(Test)`} + {networkName} {isTestnet && `(Test)`} ) } diff --git a/app.config.js b/app.config.js index f24dab256..a657752e1 100644 --- a/app.config.js +++ b/app.config.js @@ -1,9 +1,20 @@ module.exports = { - // The default network and its associated config the app should connect to - // on start. App will automatically switch network configs when user switches - // networks in their wallet. - // Ocean Protocol contracts are deployed for: 'mainnet', 'rinkeby', 'development' - network: process.env.GATSBY_NETWORK || 'mainnet', + // URI of single metadata cache instance for all networks. + // While ocean.js includes this value for each network as part of its ConfigHelper, + // it is assumed to be the same for all networks. + // In components can be accessed with the useSiteMetadata hook: + // const { appConfig } = useSiteMetadata() + // return appConfig.metadataCacheUri + metadataCacheUri: + process.env.METADATACACHE_URI || 'https://aquarius.oceanprotocol.com', + + // List of chainIds which metadata cache queries will return by default. + // This preselects the Chains user preferences. + chainIds: [1, 137, 56], + + // List of all supported chainIds. Used to populate the Chains user preferences list. + chainIdsSupported: [1, 3, 4, 137, 80001, 1287, 56], + rbacUrl: process.env.GATSBY_RBAC_URL, infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx', diff --git a/codegen.yml b/codegen.yml new file mode 100644 index 000000000..162491320 --- /dev/null +++ b/codegen.yml @@ -0,0 +1,22 @@ +overwrite: true +schema: 'https://subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph' +documents: + - './src/utils/subgraph.ts' + - './src/components/pages/History/PoolShares.tsx' + - './src/components/pages/History/Downloads.tsx' + - './src/components/pages/History/ComputeJobs/index.tsx' + - './src/ //Users/bogdanfazakas/Sites/ocean-market/src/components/organisms/AssetContent/EditHistory.tsx' + # - './src/components/organisms/AssetActions/Pool/index.tsx' + - './src/components/organisms/AssetActions/Pool/Graph.tsx' + - './src/components/organisms/AssetActions/Consume.tsx' + - './src/components/molecules/PoolTransactions.tsx' + - './src/components/molecules/MarketStats.tsx' +generates: + ./src/@types/graph.types.ts: + plugins: + - 'typescript' + - 'typescript-operations' + - 'typescript-react-apollo' + ./graphql.schema.json: + plugins: + - 'introspection' diff --git a/content/pages/publish/index.json b/content/pages/publish/index.json index 00d2d5d91..666ff4815 100644 --- a/content/pages/publish/index.json +++ b/content/pages/publish/index.json @@ -1,5 +1,6 @@ { "title": "Publish", "description": "Highlight the important features of your data set or algorithm to make it more discoverable and catch the interest of data consumers.", - "warning": "Given the beta status, publishing on Ropsten or Rinkeby first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms)." + "warning": "Given the beta status, publishing on Ropsten or Rinkeby first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms).", + "tooltipNetwork": "Assets are published into the network your wallet is connected to. Switch your wallet's network to publish into another one." } diff --git a/gatsby-node.js b/gatsby-node.js index 6c69223f1..86b1e751d 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -7,6 +7,9 @@ execSync(`node ./scripts/write-repo-metadata > repo-metadata.json`, { stdio: 'inherit' }) +// Generate GraphQl typings for urql +// execSync(`npm run graphql:graphTypes`, { stdio: 'inherit' }) + // Generate Apollo typings execSync(`npm run apollo:codegen`, { stdio: 'inherit' }) diff --git a/package-lock.json b/package-lock.json index 29fb0e58d..0fa6509dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@apollo/client": "^3.3.19", "@coingecko/cryptoformat": "^0.4.2", "@loadable/component": "^5.15.0", "@oceanprotocol/art": "^3.0.0", @@ -19,6 +18,7 @@ "@portis/web3": "^4.0.4", "@sindresorhus/slugify": "^2.1.0", "@tippyjs/react": "^4.2.5", + "@urql/introspection": "^0.3.0", "@walletconnect/web3-provider": "^1.5.0-rc.7", "axios": "^0.21.1", "chart.js": "^2.9.4", @@ -47,6 +47,7 @@ "gatsby-transformer-remark": "^2.16.1", "gatsby-transformer-sharp": "^2.12.1", "graphql": "14.7.0", + "graphql-schema-typescript": "^1.5.2", "is-url-superb": "^6.0.0", "jwt-decode": "^3.1.2", "lodash.debounce": "^4.0.8", @@ -68,12 +69,18 @@ "shortid": "^2.2.16", "slugify": "^1.5.3", "swr": "^0.5.6", + "urql": "^2.0.3", "use-dark-mode": "^2.3.1", "web3": "^1.4.0", "web3modal": "^1.9.3", "yup": "^0.32.9" }, "devDependencies": { + "@graphql-codegen/cli": "1.21.6", + "@graphql-codegen/introspection": "1.18.2", + "@graphql-codegen/typescript": "1.22.4", + "@graphql-codegen/typescript-operations": "^1.18.3", + "@graphql-codegen/typescript-react-apollo": "2.3.0", "@svgr/webpack": "^5.5.0", "@testing-library/jest-dom": "^5.12.0", "@testing-library/react": "^11.2.7", @@ -903,7 +910,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz", "integrity": "sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1171,7 +1178,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz", "integrity": "sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-flow": "^7.14.5" @@ -2771,6 +2778,587 @@ "@ethersproject/strings": "^5.4.0" } }, + "node_modules/@graphql-codegen/cli": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.21.6.tgz", + "integrity": "sha512-wtBk4lk/YxG6MrxnBOxE9nCfR9PNDjaqA8CF9hi6Q8jiSl4sV03tC2R+gE7+2EI8J6Xa1bxZe15LnBhVwb/mUA==", + "dev": true, + "dependencies": { + "@graphql-codegen/core": "1.17.10", + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/apollo-engine-loader": "^6.2.5", + "@graphql-tools/code-file-loader": "^6.3.1", + "@graphql-tools/git-loader": "^6.2.6", + "@graphql-tools/github-loader": "^6.2.5", + "@graphql-tools/graphql-file-loader": "^6.2.7", + "@graphql-tools/json-file-loader": "^6.2.6", + "@graphql-tools/load": "^6.2.8", + "@graphql-tools/prisma-loader": "^6.3.0", + "@graphql-tools/url-loader": "^6.10.1", + "@graphql-tools/utils": "^7.9.1", + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "change-case-all": "1.0.14", + "chokidar": "^3.5.2", + "common-tags": "^1.8.0", + "cosmiconfig": "^7.0.0", + "debounce": "^1.2.0", + "dependency-graph": "^0.11.0", + "detect-indent": "^6.0.0", + "glob": "^7.1.6", + "graphql-config": "^3.3.0", + "inquirer": "^7.3.3", + "is-glob": "^4.0.1", + "json-to-pretty-yaml": "^1.2.2", + "latest-version": "5.1.0", + "listr": "^0.14.3", + "listr-update-renderer": "^0.5.0", + "log-symbols": "^4.0.0", + "minimatch": "^3.0.4", + "mkdirp": "^1.0.4", + "string-env-interpolation": "^1.0.1", + "ts-log": "^2.2.3", + "tslib": "~2.3.0", + "valid-url": "^1.0.9", + "wrap-ansi": "^7.0.0", + "yaml": "^1.10.0", + "yargs": "^17.0.0" + }, + "bin": { + "gql-gen": "bin.js", + "graphql-code-generator": "bin.js", + "graphql-codegen": "bin.js" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/@graphql-codegen/cli/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@graphql-codegen/cli/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/@graphql-codegen/cli/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/@graphql-codegen/cli/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-codegen/cli/node_modules/yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@graphql-codegen/core": { + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-1.17.10.tgz", + "integrity": "sha512-RA3umgVDs/RI/+ztHh+H4GfJxrJUfWJQqoAkMfX4qPTVO5qsy3R4vPudE0oP8w+kFbL8dFsRfAAPUZxI4jV/hQ==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/merge": "^6.2.14", + "@graphql-tools/utils": "^7.9.1", + "tslib": "~2.2.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/core/node_modules/tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + }, + "node_modules/@graphql-codegen/introspection": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-1.18.2.tgz", + "integrity": "sha512-1SuhWu6nbOIOSZdQLNmHHVN8VsTavUQUD6OWjUuR3Bjpw9jfykNYXPpGGQxdCa5hmiWvn2JQU+qtgtERzz4+rg==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.5", + "tslib": "~2.2.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/introspection/node_modules/tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + }, + "node_modules/@graphql-codegen/plugin-helpers": { + "version": "1.18.8", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.8.tgz", + "integrity": "sha512-mb4I9j9lMGqvGggYuZ0CV+Hme08nar68xkpPbAVotg/ZBmlhZIok/HqW2BcMQi7Rj+Il5HQMeQ1wQ1M7sv/TlQ==", + "dev": true, + "dependencies": { + "@graphql-tools/utils": "^7.9.1", + "common-tags": "1.8.0", + "import-from": "4.0.0", + "lodash": "~4.17.0", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/plugin-helpers/node_modules/import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true, + "engines": { + "node": ">=12.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@graphql-codegen/plugin-helpers/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-codegen/typescript": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.22.4.tgz", + "integrity": "sha512-39g71++31Ki9AufBRPz4iHAZ3nboD6m+UJonYk/l3LrQEWcVxtNZXvUaoCyHSvKIysOv3IppqonrQf9eJACOdA==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-codegen/visitor-plugin-common": "1.21.3", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/typescript-operations": { + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.4.tgz", + "integrity": "sha512-bxeRaCCwu2rUXkRj6WwMVazlMignemeUJfDjrK7d4z9o9tyjlrGWnbsjeZI7M17GNCARU9Vkr6XH94wEyooSsA==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-codegen/typescript": "^1.23.0", + "@graphql-codegen/visitor-plugin-common": "1.22.0", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/typescript-operations/node_modules/@graphql-codegen/typescript": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.23.0.tgz", + "integrity": "sha512-ZfFgk5mGfuOy4kEpy+dcuvJMphigMfJ4AkiP1qWmWFufDW3Sg2yayTSNmzeFdcXMrWGgfNW2dKtuuTmbmQhS5g==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-codegen/visitor-plugin-common": "1.22.0", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/typescript-operations/node_modules/@graphql-codegen/visitor-plugin-common": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.22.0.tgz", + "integrity": "sha512-2afJGb6d8iuZl9KizYsexPwraEKO1lAvt5eVHNM5Xew4vwz/AUHeqDR2uOeQgVV+27EzjjzSDd47IEdH0dLC2w==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-tools/optimize": "^1.0.1", + "@graphql-tools/relay-operation-optimizer": "^6.3.0", + "array.prototype.flatmap": "^1.2.4", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "dependency-graph": "^0.11.0", + "graphql-tag": "^2.11.0", + "parse-filepath": "^1.0.2", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/typescript-operations/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-codegen/typescript-react-apollo": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-2.3.0.tgz", + "integrity": "sha512-x3QlANKIg1aVMHu+lz9aZJJWB2wVUDBrRZ+7NhTNR8Pm1XEOcrU+JyACPAwUuHZ1/lV2nJg1u0+AMrFCsLeL8A==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-codegen/visitor-plugin-common": "1.21.3", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", + "graphql-tag": "^2.0.0" + } + }, + "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-codegen/typescript/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-codegen/visitor-plugin-common": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.21.3.tgz", + "integrity": "sha512-GBQ9eHoaMjXGXJpAPJ/6b/pDAlO9r+6TDVibJVGn7qI+B6ScR4kO4AyZKuAgSL0TaJitpb93LCVoMX+ibRJwxg==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/optimize": "^1.0.1", + "@graphql-tools/relay-operation-optimizer": "^6.3.0", + "array.prototype.flatmap": "^1.2.4", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "dependency-graph": "^0.11.0", + "graphql-tag": "^2.11.0", + "parse-filepath": "^1.0.2", + "tslib": "~2.3.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-codegen/visitor-plugin-common/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "node_modules/@graphql-tools/apollo-engine-loader": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-6.2.5.tgz", + "integrity": "sha512-CE4uef6PyxtSG+7OnLklIr2BZZDgjO89ZXK47EKdY7jQy/BQD/9o+8SxPsgiBc+2NsDJH2I6P/nqoaJMOEat6g==", + "dev": true, + "dependencies": { + "@graphql-tools/utils": "^7.0.0", + "cross-fetch": "3.0.6", + "tslib": "~2.0.1" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-tools/apollo-engine-loader/node_modules/cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "dev": true, + "dependencies": { + "node-fetch": "2.6.1" + } + }, + "node_modules/@graphql-tools/apollo-engine-loader/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + }, "node_modules/@graphql-tools/batch-delegate": { "version": "6.2.6", "resolved": "https://registry.npmjs.org/@graphql-tools/batch-delegate/-/batch-delegate-6.2.6.tgz", @@ -2874,7 +3462,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-6.3.1.tgz", "integrity": "sha512-ZJimcm2ig+avgsEOWWVvAaxZrXXhiiSZyYYOJi0hk9wh5BxZcLUNKkTp6EFnZE/jmGUwuos3pIjUD3Hwi3Bwhg==", - "optional": true, + "devOptional": true, "dependencies": { "@graphql-tools/graphql-tag-pluck": "^6.5.1", "@graphql-tools/utils": "^7.0.0", @@ -2888,7 +3476,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true }, "node_modules/@graphql-tools/delegate": { "version": "7.1.5", @@ -2916,7 +3504,7 @@ "version": "6.2.6", "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-6.2.6.tgz", "integrity": "sha512-ooQTt2CaG47vEYPP3CPD+nbA0F+FYQXfzrB1Y1ABN9K3d3O2RK3g8qwslzZaI8VJQthvKwt0A95ZeE4XxteYfw==", - "optional": true, + "devOptional": true, "dependencies": { "@graphql-tools/graphql-tag-pluck": "^6.2.6", "@graphql-tools/utils": "^7.0.0", @@ -2930,13 +3518,13 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true }, "node_modules/@graphql-tools/github-loader": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz", "integrity": "sha512-DLuQmYeNNdPo8oWus8EePxWCfCAyUXPZ/p1PWqjrX/NGPyH2ZObdqtDAfRHztljt0F/qkBHbGHCEk2TKbRZTRw==", - "optional": true, + "devOptional": true, "dependencies": { "@graphql-tools/graphql-tag-pluck": "^6.2.6", "@graphql-tools/utils": "^7.0.0", @@ -2951,7 +3539,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", - "optional": true, + "devOptional": true, "dependencies": { "node-fetch": "2.6.1" } @@ -2960,7 +3548,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "optional": true + "devOptional": true }, "node_modules/@graphql-tools/graphql-file-loader": { "version": "6.2.7", @@ -2984,7 +3572,7 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.5.1.tgz", "integrity": "sha512-7qkm82iFmcpb8M6/yRgzjShtW6Qu2OlCSZp8uatA3J0eMl87TxyJoUmL3M3UMMOSundAK8GmoyNVFUrueueV5Q==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/parser": "7.12.16", "@babel/traverse": "7.12.13", @@ -3000,7 +3588,7 @@ "version": "7.12.16", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", - "optional": true, + "devOptional": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -3012,7 +3600,7 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.12.13", @@ -3029,7 +3617,7 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -3040,7 +3628,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true }, "node_modules/@graphql-tools/import": { "version": "6.3.1", @@ -3264,11 +3852,158 @@ "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "optional": true }, + "node_modules/@graphql-tools/optimize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.0.1.tgz", + "integrity": "sha512-cRlUNsbErYoBtzzS6zXahXeTBZGPVlPHXCpnEZ0XiK/KY/sQL96cyzak0fM/Gk6qEI9/l32MYEICjasiBQrl5w==", + "dev": true, + "dependencies": { + "tslib": "~2.0.1" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-tools/optimize/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + }, + "node_modules/@graphql-tools/prisma-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-6.3.0.tgz", + "integrity": "sha512-9V3W/kzsFBmUQqOsd96V4a4k7Didz66yh/IK89B1/rrvy9rYj+ULjEqR73x9BYZ+ww9FV8yP8LasWAJwWaqqJQ==", + "dev": true, + "dependencies": { + "@graphql-tools/url-loader": "^6.8.2", + "@graphql-tools/utils": "^7.0.0", + "@types/http-proxy-agent": "^2.0.2", + "@types/js-yaml": "^4.0.0", + "@types/json-stable-stringify": "^1.0.32", + "@types/jsonwebtoken": "^8.5.0", + "chalk": "^4.1.0", + "debug": "^4.3.1", + "dotenv": "^8.2.0", + "graphql-request": "^3.3.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "isomorphic-fetch": "^3.0.0", + "js-yaml": "^4.0.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.20", + "replaceall": "^0.1.6", + "scuid": "^1.1.0", + "tslib": "~2.1.0", + "yaml-ast-parser": "^0.0.43" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, "node_modules/@graphql-tools/relay-operation-optimizer": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz", "integrity": "sha512-Or3UgRvkY9Fq1AAx7q38oPqFmTepLz7kp6wDHKyR0ceG7AvHv5En22R12mAeISInbhff4Rpwgf6cE8zHRu6bCw==", - "optional": true, + "devOptional": true, "dependencies": { "@graphql-tools/utils": "^7.1.0", "relay-compiler": "10.1.0", @@ -3282,7 +4017,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "optional": true + "devOptional": true }, "node_modules/@graphql-tools/resolvers-composition": { "version": "6.2.8", @@ -8713,6 +9448,15 @@ "@types/node": "*" } }, + "node_modules/@types/http-proxy-agent": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz", + "integrity": "sha512-2S6IuBRhqUnH1/AUx9k8KWtY3Esg4eqri946MnxTG5HwehF1S5mqLln8fcyMiuQkY72p2gH3W+rIPqp5li0LyQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -8745,6 +9489,12 @@ "pretty-format": "^26.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.2.tgz", + "integrity": "sha512-KbeHS/Y4R+k+5sWXEYzAZKuB1yQlZtEghuhRxrVRLaqhtoG5+26JwQsa4HyS3AWX8v1Uwukma5HheduUDskasA==", + "dev": true + }, "node_modules/@types/json-patch": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/json-patch/-/json-patch-0.0.30.tgz", @@ -8755,11 +9505,26 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, + "node_modules/@types/json-stable-stringify": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz", + "integrity": "sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", + "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", @@ -9317,6 +10082,26 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@urql/core": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-2.1.5.tgz", + "integrity": "sha512-iX30xZ6exdJbajv2S4gxVP+1ZEh57x9gEuUrpOCLWZp84970XDSo82xoz+11I+NGWiQNZfbIs6LTLaie/iG3OQ==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.0", + "wonka": "^4.0.14" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/@urql/introspection": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@urql/introspection/-/introspection-0.3.0.tgz", + "integrity": "sha512-dw87YiSmCgEUFnWj7fYu+wnrFcSEhYyQ8DykTvL1UzCKPm9uLDeey1z0yiSxZeX2qP9oD+TSGIq+61AZHhQO0w==", + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, "node_modules/@use-it/event-listener": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@use-it/event-listener/-/event-listener-0.1.6.tgz", @@ -11147,7 +11932,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "optional": true + "devOptional": true }, "node_modules/asn1": { "version": "0.2.4", @@ -11383,6 +12168,18 @@ "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" }, + "node_modules/auto-bind": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/autoprefixer": { "version": "9.8.6", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", @@ -11894,7 +12691,7 @@ "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "optional": true + "devOptional": true }, "node_modules/babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", @@ -11928,7 +12725,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", @@ -13471,6 +14268,12 @@ "node": ">=0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -13972,6 +14775,75 @@ "tslib": "^2.0.3" } }, + "node_modules/change-case-all": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz", + "integrity": "sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==", + "dev": true, + "dependencies": { + "change-case": "^4.1.2", + "is-lower-case": "^2.0.2", + "is-upper-case": "^2.0.2", + "lower-case": "^2.0.2", + "lower-case-first": "^2.0.2", + "sponge-case": "^1.0.1", + "swap-case": "^2.0.2", + "title-case": "^3.0.3", + "upper-case": "^2.0.2", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/change-case-all/node_modules/is-lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", + "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/change-case-all/node_modules/is-upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", + "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/change-case-all/node_modules/lower-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", + "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/change-case-all/node_modules/swap-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", + "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/change-case-all/node_modules/title-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", + "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/change-case-all/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, "node_modules/change-case/node_modules/tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", @@ -14265,23 +15137,23 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/chownr": { @@ -16235,6 +17107,12 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "node_modules/debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -16821,6 +17699,15 @@ "node": ">= 0.6" } }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/deprecated-decorator": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", @@ -17459,6 +18346,15 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ed2curve": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", @@ -21441,7 +22337,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", - "optional": true, + "devOptional": true, "dependencies": { "cross-fetch": "^3.0.4", "fbjs-css-vars": "^1.0.0", @@ -21456,7 +22352,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "optional": true + "devOptional": true }, "node_modules/fd": { "version": "0.0.3", @@ -26781,6 +27677,36 @@ "express": "^4.16.2" } }, + "node_modules/graphql-request": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz", + "integrity": "sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg==", + "dev": true, + "dependencies": { + "cross-fetch": "^3.0.6", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x" + } + }, + "node_modules/graphql-schema-typescript": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/graphql-schema-typescript/-/graphql-schema-typescript-1.5.2.tgz", + "integrity": "sha512-r09ycu9BLSeRg8VcEB1Jg2kHeBxeDOdz1iyhA/rja3IcZoau3MRie0203piWI8j+N/KIv60OBPquiEgq1snYAQ==", + "dependencies": { + "camelcase": "^6.2.0", + "yargs": "^16.0.0" + }, + "bin": { + "graphql-schema-typescript": "lib/cli.js" + }, + "peerDependencies": { + "graphql": "^14.0.0", + "typescript": "*" + } + }, "node_modules/graphql-subscriptions": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz", @@ -28541,7 +29467,7 @@ "version": "3.7.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=", - "optional": true, + "devOptional": true, "engines": { "node": ">=0.8.0" } @@ -29800,6 +30726,19 @@ "multibase": "^4.0.1" } }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -30603,6 +31542,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-upper-case": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", @@ -30763,6 +31714,22 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/isomorphic-fetch/node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "dev": true + }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -33313,6 +34280,19 @@ "delimit-stream": "0.1.0" } }, + "node_modules/json-to-pretty-yaml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", + "integrity": "sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs=", + "dev": true, + "dependencies": { + "remedial": "^1.0.7", + "remove-trailing-spaces": "^1.0.6" + }, + "engines": { + "node": ">= 0.2.0" + } + }, "node_modules/json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -33377,6 +34357,37 @@ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -33411,6 +34422,27 @@ "node": ">=8" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/jwt-decode": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", @@ -34474,16 +35506,46 @@ "integrity": "sha1-rXvGpOZH15yXLhuA/u968VYmeHY=", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", + "dev": true + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", + "dev": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", + "dev": true + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, "node_modules/lodash.keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", @@ -34515,6 +35577,12 @@ "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, "node_modules/lodash.orderby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz", @@ -38047,7 +39115,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "optional": true + "devOptional": true }, "node_modules/num2fraction": { "version": "1.2.2", @@ -39202,6 +40270,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -39547,6 +40629,27 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -41950,7 +43053,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "optional": true, + "devOptional": true, "dependencies": { "asap": "~2.0.3" } @@ -43117,9 +44220,9 @@ } }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dependencies": { "picomatch": "^2.2.1" }, @@ -43453,7 +44556,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-10.1.0.tgz", "integrity": "sha512-HPqc3N3tNgEgUH5+lTr5lnLbgnsZMt+MRiyS0uAVNhuPY2It0X1ZJG+9qdA3L9IqKFUNwVn6zTO7RArjMZbARQ==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/core": "^7.0.0", "@babel/generator": "^7.5.0", @@ -43483,7 +44586,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": true, + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -43498,7 +44601,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "optional": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -43507,7 +44610,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "optional": true, + "devOptional": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -43523,7 +44626,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "optional": true, + "devOptional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -43534,7 +44637,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -43546,13 +44649,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true + "devOptional": true }, "node_modules/relay-compiler/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "optional": true, + "devOptional": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -43565,7 +44668,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -43574,7 +44677,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -43583,7 +44686,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "optional": true, + "devOptional": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -43595,7 +44698,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "optional": true, + "devOptional": true, "dependencies": { "p-try": "^2.0.0" }, @@ -43610,7 +44713,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "optional": true, + "devOptional": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -43622,7 +44725,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -43631,7 +44734,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "optional": true, + "devOptional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -43645,7 +44748,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "optional": true, + "devOptional": true, "dependencies": { "ansi-regex": "^5.0.0" }, @@ -43657,7 +44760,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "optional": true, + "devOptional": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -43669,7 +44772,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "optional": true, + "devOptional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -43683,13 +44786,13 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "optional": true + "devOptional": true }, "node_modules/relay-compiler/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "optional": true, + "devOptional": true, "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -43711,7 +44814,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "optional": true, + "devOptional": true, "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -43724,7 +44827,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-10.1.0.tgz", "integrity": "sha512-bxznLnQ1ST6APN/cFi7l0FpjbZVchWQjjhj9mAuJBuUqNNCh9uV+UTRhpQF7Q8ycsPp19LHTpVyGhYb0ustuRQ==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/runtime": "^7.0.0", "fbjs": "^3.0.0" @@ -44090,6 +45193,15 @@ "unist-util-stringify-position": "^1.1.1" } }, + "node_modules/remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/remote-redux-devtools": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz", @@ -44122,6 +45234,12 @@ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, + "node_modules/remove-trailing-spaces": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", + "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", + "dev": true + }, "node_modules/renderkid": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", @@ -44188,6 +45306,15 @@ "node": ">= 0.10" } }, + "node_modules/replaceall": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", + "integrity": "sha1-gdgax663LX9cSUKt8ml6MiBojY4=", + "dev": true, + "engines": { + "node": ">= 0.8.x" + } + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -45206,6 +46333,12 @@ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" }, + "node_modules/scuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", + "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", + "dev": true + }, "node_modules/secp256k1": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", @@ -46153,7 +47286,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", "integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo=", - "optional": true + "devOptional": true }, "node_modules/simple-concat": { "version": "1.0.1", @@ -47363,6 +48496,21 @@ "node": ">=0.10.0" } }, + "node_modules/sponge-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", + "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/sponge-case/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -49517,6 +50665,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" }, + "node_modules/ts-log": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.3.tgz", + "integrity": "sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w==", + "dev": true + }, "node_modules/ts-node": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", @@ -49752,6 +50906,7 @@ "version": "0.7.28", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -49762,7 +50917,6 @@ "url": "https://paypal.me/faisalman" } ], - "optional": true, "engines": { "node": "*" } @@ -50750,6 +51904,19 @@ "node": ">=0.4.x" } }, + "node_modules/urql": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/urql/-/urql-2.0.4.tgz", + "integrity": "sha512-ARITb+l+DsGbK/y3mxQjbC4lw8Z4IqFfk4Va8Eg1rDpN1HhOkMTxFA/YvGZoQIU1re/SsARLe//TEBtsgH0/CQ==", + "dependencies": { + "@urql/core": "^2.1.4", + "wonka": "^4.0.14" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/ursa-optional": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/ursa-optional/-/ursa-optional-0.10.2.tgz", @@ -53698,6 +54865,11 @@ "node": ">= 0.10.0" } }, + "node_modules/wonka": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", + "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==" + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -54077,6 +55249,12 @@ "node": ">= 6" } }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true + }, "node_modules/yaml-loader": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.6.0.tgz", @@ -55299,7 +56477,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz", "integrity": "sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q==", - "optional": true, + "devOptional": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -55495,7 +56673,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz", "integrity": "sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA==", - "optional": true, + "devOptional": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-flow": "^7.14.5" @@ -56640,6 +57818,485 @@ "@ethersproject/strings": "^5.4.0" } }, + "@graphql-codegen/cli": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.21.6.tgz", + "integrity": "sha512-wtBk4lk/YxG6MrxnBOxE9nCfR9PNDjaqA8CF9hi6Q8jiSl4sV03tC2R+gE7+2EI8J6Xa1bxZe15LnBhVwb/mUA==", + "dev": true, + "requires": { + "@graphql-codegen/core": "1.17.10", + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/apollo-engine-loader": "^6.2.5", + "@graphql-tools/code-file-loader": "^6.3.1", + "@graphql-tools/git-loader": "^6.2.6", + "@graphql-tools/github-loader": "^6.2.5", + "@graphql-tools/graphql-file-loader": "^6.2.7", + "@graphql-tools/json-file-loader": "^6.2.6", + "@graphql-tools/load": "^6.2.8", + "@graphql-tools/prisma-loader": "^6.3.0", + "@graphql-tools/url-loader": "^6.10.1", + "@graphql-tools/utils": "^7.9.1", + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "change-case-all": "1.0.14", + "chokidar": "^3.5.2", + "common-tags": "^1.8.0", + "cosmiconfig": "^7.0.0", + "debounce": "^1.2.0", + "dependency-graph": "^0.11.0", + "detect-indent": "^6.0.0", + "glob": "^7.1.6", + "graphql-config": "^3.3.0", + "inquirer": "^7.3.3", + "is-glob": "^4.0.1", + "json-to-pretty-yaml": "^1.2.2", + "latest-version": "5.1.0", + "listr": "^0.14.3", + "listr-update-renderer": "^0.5.0", + "log-symbols": "^4.0.0", + "minimatch": "^3.0.4", + "mkdirp": "^1.0.4", + "string-env-interpolation": "^1.0.1", + "ts-log": "^2.2.3", + "tslib": "~2.3.0", + "valid-url": "^1.0.9", + "wrap-ansi": "^7.0.0", + "yaml": "^1.10.0", + "yargs": "^17.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + }, + "yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "@graphql-codegen/core": { + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-1.17.10.tgz", + "integrity": "sha512-RA3umgVDs/RI/+ztHh+H4GfJxrJUfWJQqoAkMfX4qPTVO5qsy3R4vPudE0oP8w+kFbL8dFsRfAAPUZxI4jV/hQ==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/merge": "^6.2.14", + "@graphql-tools/utils": "^7.9.1", + "tslib": "~2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + } + } + }, + "@graphql-codegen/introspection": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-1.18.2.tgz", + "integrity": "sha512-1SuhWu6nbOIOSZdQLNmHHVN8VsTavUQUD6OWjUuR3Bjpw9jfykNYXPpGGQxdCa5hmiWvn2JQU+qtgtERzz4+rg==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.5", + "tslib": "~2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + } + } + }, + "@graphql-codegen/plugin-helpers": { + "version": "1.18.8", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.8.tgz", + "integrity": "sha512-mb4I9j9lMGqvGggYuZ0CV+Hme08nar68xkpPbAVotg/ZBmlhZIok/HqW2BcMQi7Rj+Il5HQMeQ1wQ1M7sv/TlQ==", + "dev": true, + "requires": { + "@graphql-tools/utils": "^7.9.1", + "common-tags": "1.8.0", + "import-from": "4.0.0", + "lodash": "~4.17.0", + "tslib": "~2.3.0" + }, + "dependencies": { + "import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, + "@graphql-codegen/typescript": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.22.4.tgz", + "integrity": "sha512-39g71++31Ki9AufBRPz4iHAZ3nboD6m+UJonYk/l3LrQEWcVxtNZXvUaoCyHSvKIysOv3IppqonrQf9eJACOdA==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-codegen/visitor-plugin-common": "1.21.3", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, + "@graphql-codegen/typescript-operations": { + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.4.tgz", + "integrity": "sha512-bxeRaCCwu2rUXkRj6WwMVazlMignemeUJfDjrK7d4z9o9tyjlrGWnbsjeZI7M17GNCARU9Vkr6XH94wEyooSsA==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-codegen/typescript": "^1.23.0", + "@graphql-codegen/visitor-plugin-common": "1.22.0", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + }, + "dependencies": { + "@graphql-codegen/typescript": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.23.0.tgz", + "integrity": "sha512-ZfFgk5mGfuOy4kEpy+dcuvJMphigMfJ4AkiP1qWmWFufDW3Sg2yayTSNmzeFdcXMrWGgfNW2dKtuuTmbmQhS5g==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-codegen/visitor-plugin-common": "1.22.0", + "auto-bind": "~4.0.0", + "tslib": "~2.3.0" + } + }, + "@graphql-codegen/visitor-plugin-common": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.22.0.tgz", + "integrity": "sha512-2afJGb6d8iuZl9KizYsexPwraEKO1lAvt5eVHNM5Xew4vwz/AUHeqDR2uOeQgVV+27EzjjzSDd47IEdH0dLC2w==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-tools/optimize": "^1.0.1", + "@graphql-tools/relay-operation-optimizer": "^6.3.0", + "array.prototype.flatmap": "^1.2.4", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "dependency-graph": "^0.11.0", + "graphql-tag": "^2.11.0", + "parse-filepath": "^1.0.2", + "tslib": "~2.3.0" + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, + "@graphql-codegen/typescript-react-apollo": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-2.3.0.tgz", + "integrity": "sha512-x3QlANKIg1aVMHu+lz9aZJJWB2wVUDBrRZ+7NhTNR8Pm1XEOcrU+JyACPAwUuHZ1/lV2nJg1u0+AMrFCsLeL8A==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-codegen/visitor-plugin-common": "1.21.3", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "tslib": "~2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, + "@graphql-codegen/visitor-plugin-common": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.21.3.tgz", + "integrity": "sha512-GBQ9eHoaMjXGXJpAPJ/6b/pDAlO9r+6TDVibJVGn7qI+B6ScR4kO4AyZKuAgSL0TaJitpb93LCVoMX+ibRJwxg==", + "dev": true, + "requires": { + "@graphql-codegen/plugin-helpers": "^1.18.7", + "@graphql-tools/optimize": "^1.0.1", + "@graphql-tools/relay-operation-optimizer": "^6.3.0", + "array.prototype.flatmap": "^1.2.4", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.14", + "dependency-graph": "^0.11.0", + "graphql-tag": "^2.11.0", + "parse-filepath": "^1.0.2", + "tslib": "~2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, + "@graphql-tools/apollo-engine-loader": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-6.2.5.tgz", + "integrity": "sha512-CE4uef6PyxtSG+7OnLklIr2BZZDgjO89ZXK47EKdY7jQy/BQD/9o+8SxPsgiBc+2NsDJH2I6P/nqoaJMOEat6g==", + "dev": true, + "requires": { + "@graphql-tools/utils": "^7.0.0", + "cross-fetch": "3.0.6", + "tslib": "~2.0.1" + }, + "dependencies": { + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "dev": true, + "requires": { + "node-fetch": "2.6.1" + } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, "@graphql-tools/batch-delegate": { "version": "6.2.6", "resolved": "https://registry.npmjs.org/@graphql-tools/batch-delegate/-/batch-delegate-6.2.6.tgz", @@ -56734,7 +58391,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-6.3.1.tgz", "integrity": "sha512-ZJimcm2ig+avgsEOWWVvAaxZrXXhiiSZyYYOJi0hk9wh5BxZcLUNKkTp6EFnZE/jmGUwuos3pIjUD3Hwi3Bwhg==", - "optional": true, + "devOptional": true, "requires": { "@graphql-tools/graphql-tag-pluck": "^6.5.1", "@graphql-tools/utils": "^7.0.0", @@ -56745,7 +58402,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true } } }, @@ -56774,7 +58431,7 @@ "version": "6.2.6", "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-6.2.6.tgz", "integrity": "sha512-ooQTt2CaG47vEYPP3CPD+nbA0F+FYQXfzrB1Y1ABN9K3d3O2RK3g8qwslzZaI8VJQthvKwt0A95ZeE4XxteYfw==", - "optional": true, + "devOptional": true, "requires": { "@graphql-tools/graphql-tag-pluck": "^6.2.6", "@graphql-tools/utils": "^7.0.0", @@ -56785,7 +58442,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true } } }, @@ -56793,7 +58450,7 @@ "version": "6.2.5", "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz", "integrity": "sha512-DLuQmYeNNdPo8oWus8EePxWCfCAyUXPZ/p1PWqjrX/NGPyH2ZObdqtDAfRHztljt0F/qkBHbGHCEk2TKbRZTRw==", - "optional": true, + "devOptional": true, "requires": { "@graphql-tools/graphql-tag-pluck": "^6.2.6", "@graphql-tools/utils": "^7.0.0", @@ -56805,7 +58462,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", - "optional": true, + "devOptional": true, "requires": { "node-fetch": "2.6.1" } @@ -56814,7 +58471,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "optional": true + "devOptional": true } } }, @@ -56839,7 +58496,7 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.5.1.tgz", "integrity": "sha512-7qkm82iFmcpb8M6/yRgzjShtW6Qu2OlCSZp8uatA3J0eMl87TxyJoUmL3M3UMMOSundAK8GmoyNVFUrueueV5Q==", - "optional": true, + "devOptional": true, "requires": { "@babel/parser": "7.12.16", "@babel/traverse": "7.12.13", @@ -56852,13 +58509,13 @@ "version": "7.12.16", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", - "optional": true + "devOptional": true }, "@babel/traverse": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", - "optional": true, + "devOptional": true, "requires": { "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.12.13", @@ -56875,7 +58532,7 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", - "optional": true, + "devOptional": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -56886,7 +58543,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "optional": true + "devOptional": true } } }, @@ -57096,11 +58753,129 @@ } } }, + "@graphql-tools/optimize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.0.1.tgz", + "integrity": "sha512-cRlUNsbErYoBtzzS6zXahXeTBZGPVlPHXCpnEZ0XiK/KY/sQL96cyzak0fM/Gk6qEI9/l32MYEICjasiBQrl5w==", + "dev": true, + "requires": { + "tslib": "~2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@graphql-tools/prisma-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-6.3.0.tgz", + "integrity": "sha512-9V3W/kzsFBmUQqOsd96V4a4k7Didz66yh/IK89B1/rrvy9rYj+ULjEqR73x9BYZ+ww9FV8yP8LasWAJwWaqqJQ==", + "dev": true, + "requires": { + "@graphql-tools/url-loader": "^6.8.2", + "@graphql-tools/utils": "^7.0.0", + "@types/http-proxy-agent": "^2.0.2", + "@types/js-yaml": "^4.0.0", + "@types/json-stable-stringify": "^1.0.32", + "@types/jsonwebtoken": "^8.5.0", + "chalk": "^4.1.0", + "debug": "^4.3.1", + "dotenv": "^8.2.0", + "graphql-request": "^3.3.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "isomorphic-fetch": "^3.0.0", + "js-yaml": "^4.0.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.20", + "replaceall": "^0.1.6", + "scuid": "^1.1.0", + "tslib": "~2.1.0", + "yaml-ast-parser": "^0.0.43" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, "@graphql-tools/relay-operation-optimizer": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz", "integrity": "sha512-Or3UgRvkY9Fq1AAx7q38oPqFmTepLz7kp6wDHKyR0ceG7AvHv5En22R12mAeISInbhff4Rpwgf6cE8zHRu6bCw==", - "optional": true, + "devOptional": true, "requires": { "@graphql-tools/utils": "^7.1.0", "relay-compiler": "10.1.0", @@ -57111,7 +58886,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "optional": true + "devOptional": true } } }, @@ -61626,6 +63401,15 @@ "@types/node": "*" } }, + "@types/http-proxy-agent": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz", + "integrity": "sha512-2S6IuBRhqUnH1/AUx9k8KWtY3Esg4eqri946MnxTG5HwehF1S5mqLln8fcyMiuQkY72p2gH3W+rIPqp5li0LyQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -61658,6 +63442,12 @@ "pretty-format": "^26.0.0" } }, + "@types/js-yaml": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.2.tgz", + "integrity": "sha512-KbeHS/Y4R+k+5sWXEYzAZKuB1yQlZtEghuhRxrVRLaqhtoG5+26JwQsa4HyS3AWX8v1Uwukma5HheduUDskasA==", + "dev": true + }, "@types/json-patch": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/json-patch/-/json-patch-0.0.30.tgz", @@ -61668,11 +63458,26 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, + "@types/json-stable-stringify": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz", + "integrity": "sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, + "@types/jsonwebtoken": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", + "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", @@ -62147,6 +63952,21 @@ "eslint-visitor-keys": "^2.0.0" } }, + "@urql/core": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-2.1.5.tgz", + "integrity": "sha512-iX30xZ6exdJbajv2S4gxVP+1ZEh57x9gEuUrpOCLWZp84970XDSo82xoz+11I+NGWiQNZfbIs6LTLaie/iG3OQ==", + "requires": { + "@graphql-typed-document-node/core": "^3.1.0", + "wonka": "^4.0.14" + } + }, + "@urql/introspection": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@urql/introspection/-/introspection-0.3.0.tgz", + "integrity": "sha512-dw87YiSmCgEUFnWj7fYu+wnrFcSEhYyQ8DykTvL1UzCKPm9uLDeey1z0yiSxZeX2qP9oD+TSGIq+61AZHhQO0w==", + "requires": {} + }, "@use-it/event-listener": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@use-it/event-listener/-/event-listener-0.1.6.tgz", @@ -63663,7 +65483,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "optional": true + "devOptional": true }, "asn1": { "version": "0.2.4", @@ -63887,6 +65707,12 @@ "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" }, + "auto-bind": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", + "dev": true + }, "autoprefixer": { "version": "9.8.6", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", @@ -64285,7 +66111,7 @@ "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "optional": true + "devOptional": true }, "babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", @@ -64316,7 +66142,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "optional": true, + "devOptional": true, "requires": { "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", @@ -65579,6 +67405,12 @@ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -66018,6 +67850,77 @@ } } }, + "change-case-all": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz", + "integrity": "sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==", + "dev": true, + "requires": { + "change-case": "^4.1.2", + "is-lower-case": "^2.0.2", + "is-upper-case": "^2.0.2", + "lower-case": "^2.0.2", + "lower-case-first": "^2.0.2", + "sponge-case": "^1.0.1", + "swap-case": "^2.0.2", + "title-case": "^3.0.3", + "upper-case": "^2.0.2", + "upper-case-first": "^2.0.2" + }, + "dependencies": { + "is-lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", + "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "is-upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", + "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lower-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", + "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "swap-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", + "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "title-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", + "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -66226,18 +68129,18 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "chownr": { @@ -67807,6 +69710,12 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz", "integrity": "sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==" }, + "debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -68263,6 +70172,12 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, "deprecated-decorator": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", @@ -68797,6 +70712,15 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ed2curve": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", @@ -72038,7 +73962,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", - "optional": true, + "devOptional": true, "requires": { "cross-fetch": "^3.0.4", "fbjs-css-vars": "^1.0.0", @@ -72053,7 +73977,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "optional": true + "devOptional": true }, "fd": { "version": "0.0.3", @@ -76093,6 +78017,26 @@ "graphql-playground-html": "^1.6.29" } }, + "graphql-request": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz", + "integrity": "sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg==", + "dev": true, + "requires": { + "cross-fetch": "^3.0.6", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + } + }, + "graphql-schema-typescript": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/graphql-schema-typescript/-/graphql-schema-typescript-1.5.2.tgz", + "integrity": "sha512-r09ycu9BLSeRg8VcEB1Jg2kHeBxeDOdz1iyhA/rja3IcZoau3MRie0203piWI8j+N/KIv60OBPquiEgq1snYAQ==", + "requires": { + "camelcase": "^6.2.0", + "yargs": "^16.0.0" + } + }, "graphql-subscriptions": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz", @@ -77547,7 +79491,7 @@ "version": "3.7.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=", - "optional": true + "devOptional": true }, "import-cwd": { "version": "2.1.0", @@ -78616,6 +80560,16 @@ } } }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, "is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -79158,6 +81112,12 @@ "unc-path-regex": "^0.1.2" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-upper-case": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", @@ -79280,6 +81240,24 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "requires": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + }, + "dependencies": { + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "dev": true + } + } + }, "isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -81249,6 +83227,16 @@ "delimit-stream": "0.1.0" } }, + "json-to-pretty-yaml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", + "integrity": "sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs=", + "dev": true, + "requires": { + "remedial": "^1.0.7", + "remove-trailing-spaces": "^1.0.6" + } + }, "json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -81302,6 +83290,32 @@ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -81327,6 +83341,27 @@ "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==" }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "jwt-decode": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", @@ -82232,16 +84267,46 @@ "integrity": "sha1-rXvGpOZH15yXLhuA/u968VYmeHY=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", + "dev": true + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", + "dev": true + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", + "dev": true + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, "lodash.keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", @@ -82273,6 +84338,12 @@ "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, "lodash.orderby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz", @@ -85115,7 +87186,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "optional": true + "devOptional": true }, "num2fraction": { "version": "1.2.2", @@ -86012,6 +88083,17 @@ "is-hexadecimal": "^1.0.0" } }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -86295,6 +88377,21 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -88329,7 +90426,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "optional": true, + "devOptional": true, "requires": { "asap": "~2.0.3" } @@ -89254,9 +91351,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } @@ -89529,7 +91626,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-10.1.0.tgz", "integrity": "sha512-HPqc3N3tNgEgUH5+lTr5lnLbgnsZMt+MRiyS0uAVNhuPY2It0X1ZJG+9qdA3L9IqKFUNwVn6zTO7RArjMZbARQ==", - "optional": true, + "devOptional": true, "requires": { "@babel/core": "^7.0.0", "@babel/generator": "^7.5.0", @@ -89553,7 +91650,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": true, + "devOptional": true, "requires": { "color-convert": "^2.0.1" } @@ -89562,13 +91659,13 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "optional": true + "devOptional": true }, "chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "optional": true, + "devOptional": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -89578,7 +91675,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "optional": true, + "devOptional": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -89589,7 +91686,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, + "devOptional": true, "requires": { "color-name": "~1.1.4" } @@ -89598,13 +91695,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true + "devOptional": true }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "optional": true, + "devOptional": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -89614,19 +91711,19 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "optional": true + "devOptional": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true + "devOptional": true }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "optional": true, + "devOptional": true, "requires": { "p-locate": "^4.1.0" } @@ -89635,7 +91732,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "optional": true, + "devOptional": true, "requires": { "p-try": "^2.0.0" } @@ -89644,7 +91741,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "optional": true, + "devOptional": true, "requires": { "p-limit": "^2.2.0" } @@ -89653,13 +91750,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "optional": true + "devOptional": true }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "optional": true, + "devOptional": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -89670,7 +91767,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "optional": true, + "devOptional": true, "requires": { "ansi-regex": "^5.0.0" } @@ -89679,7 +91776,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "optional": true, + "devOptional": true, "requires": { "has-flag": "^4.0.0" } @@ -89688,7 +91785,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "optional": true, + "devOptional": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -89699,13 +91796,13 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "optional": true + "devOptional": true }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "optional": true, + "devOptional": true, "requires": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -89724,7 +91821,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "optional": true, + "devOptional": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -89736,7 +91833,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-10.1.0.tgz", "integrity": "sha512-bxznLnQ1ST6APN/cFi7l0FpjbZVchWQjjhj9mAuJBuUqNNCh9uV+UTRhpQF7Q8ycsPp19LHTpVyGhYb0ustuRQ==", - "optional": true, + "devOptional": true, "requires": { "@babel/runtime": "^7.0.0", "fbjs": "^3.0.0" @@ -90057,6 +92154,12 @@ } } }, + "remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", + "dev": true + }, "remote-redux-devtools": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz", @@ -90088,6 +92191,12 @@ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, + "remove-trailing-spaces": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", + "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", + "dev": true + }, "renderkid": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", @@ -90138,6 +92247,12 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" }, + "replaceall": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", + "integrity": "sha1-gdgax663LX9cSUKt8ml6MiBojY4=", + "dev": true + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -90956,6 +93071,12 @@ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" }, + "scuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", + "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", + "dev": true + }, "secp256k1": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", @@ -91731,7 +93852,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", "integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo=", - "optional": true + "devOptional": true }, "simple-concat": { "version": "1.0.1", @@ -92699,6 +94820,23 @@ } } }, + "sponge-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", + "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true + } + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -94442,6 +96580,12 @@ } } }, + "ts-log": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.3.tgz", + "integrity": "sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w==", + "dev": true + }, "ts-node": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", @@ -94624,7 +96768,7 @@ "version": "0.7.28", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", - "optional": true + "devOptional": true }, "uglify-js": { "version": "2.8.29", @@ -95411,6 +97555,15 @@ "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, + "urql": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/urql/-/urql-2.0.4.tgz", + "integrity": "sha512-ARITb+l+DsGbK/y3mxQjbC4lw8Z4IqFfk4Va8Eg1rDpN1HhOkMTxFA/YvGZoQIU1re/SsARLe//TEBtsgH0/CQ==", + "requires": { + "@urql/core": "^2.1.4", + "wonka": "^4.0.14" + } + }, "ursa-optional": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/ursa-optional/-/ursa-optional-0.10.2.tgz", @@ -97840,6 +99993,11 @@ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" }, + "wonka": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", + "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -98134,6 +100292,12 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, + "yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true + }, "yaml-loader": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.6.0.tgz", diff --git a/package.json b/package.json index 45f101e35..8be236d1e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "gatsby build && cp _redirects public/_redirects", "serve": "serve -s public/", "jest": "NODE_ENV=test jest -c tests/unit/jest.config.js", + "test-graphql": "npm run graphql:graphTypes && npm run lint && npm run jest", "test": "npm run apollo:codegen && npm run lint && npm run jest", "test:watch": "npm run lint && npm run jest -- --watch", "lint": "npm run write:repoMetadata && eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx . && npm run type-check", @@ -19,11 +20,11 @@ "storybook:build": "build-storybook -c .storybook -o public/storybook", "write:repoMetadata": "node ./scripts/write-repo-metadata > repo-metadata.json", "deploy:s3": "./scripts/deploy-s3.sh", + "postinstall": "husky install", "apollo:codegen": "apollo client:codegen --target typescript --tsFileExtension=d.ts --outputFlat src/@types/apollo/", - "postinstall": "husky install" + "graphql:graphTypes": "graphql-codegen --config codegen.yml" }, "dependencies": { - "@apollo/client": "^3.3.19", "@coingecko/cryptoformat": "^0.4.2", "@loadable/component": "^5.15.0", "@oceanprotocol/art": "^3.0.0", @@ -32,6 +33,7 @@ "@portis/web3": "^4.0.4", "@sindresorhus/slugify": "^2.1.0", "@tippyjs/react": "^4.2.5", + "@urql/introspection": "^0.3.0", "@walletconnect/web3-provider": "^1.5.0-rc.7", "axios": "^0.21.1", "chart.js": "^2.9.4", @@ -60,6 +62,7 @@ "gatsby-transformer-remark": "^2.16.1", "gatsby-transformer-sharp": "^2.12.1", "graphql": "14.7.0", + "graphql-schema-typescript": "^1.5.2", "is-url-superb": "^6.0.0", "jwt-decode": "^3.1.2", "lodash.debounce": "^4.0.8", @@ -81,12 +84,18 @@ "shortid": "^2.2.16", "slugify": "^1.5.3", "swr": "^0.5.6", + "urql": "^2.0.3", "use-dark-mode": "^2.3.1", "web3": "^1.4.0", "web3modal": "^1.9.3", "yup": "^0.32.9" }, "devDependencies": { + "@graphql-codegen/cli": "1.21.6", + "@graphql-codegen/introspection": "1.18.2", + "@graphql-codegen/typescript": "1.22.4", + "@graphql-codegen/typescript-operations": "^1.18.3", + "@graphql-codegen/typescript-react-apollo": "2.3.0", "@svgr/webpack": "^5.5.0", "@testing-library/jest-dom": "^5.12.0", "@testing-library/react": "^11.2.7", diff --git a/src/@types/ComputeJobMetaData.d.ts b/src/@types/ComputeJobMetaData.d.ts index f3b583e9f..014dfadc4 100644 --- a/src/@types/ComputeJobMetaData.d.ts +++ b/src/@types/ComputeJobMetaData.d.ts @@ -3,4 +3,5 @@ import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Comput export interface ComputeJobMetaData extends ComputeJob { assetName: string assetDtSymbol: string + networkId: number } diff --git a/src/components/App.tsx b/src/components/App.tsx index a4fc74a0f..0f290d9f3 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -7,10 +7,9 @@ import Styles from '../global/Styles' import { useWeb3 } from '../providers/Web3' import { useSiteMetadata } from '../hooks/useSiteMetadata' import { useAccountPurgatory } from '../hooks/useAccountPurgatory' -import NetworkBanner from './molecules/NetworkBanner' -import styles from './App.module.css' import AnnouncementBanner from './atoms/AnnouncementBanner' import { useGraphSyncStatus } from '../hooks/useGraphSyncStatus' +import styles from './App.module.css' const contentQuery = graphql` query AppQuery { @@ -41,24 +40,15 @@ export default function App({ const { warning } = useSiteMetadata() const { accountId } = useWeb3() const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId) - const { isGraphSynced, blockHead, blockGraph } = useGraphSyncStatus() + // const { isGraphSynced, blockHead, blockGraph } = useGraphSyncStatus() return (
- {!isGraphSynced && ( - - )} - {!location.pathname.includes('/asset/did') && } - -
- {(props as PageProps).uri === '/' && ( - + )} +
{isInPurgatory && ( -
- {type === 'dataset' ? 'data set' : 'algorithm'} -
{accessType === 'access' ? ( ) : accessType === 'compute' && type === 'algorithm' ? ( @@ -31,6 +28,10 @@ export default function AssetType({ ) : ( )} + +
+ {type === 'dataset' ? 'data set' : 'algorithm'} +
) } diff --git a/src/components/atoms/Box.module.css b/src/components/atoms/Box.module.css index f9c4591db..cd9e4acd5 100644 --- a/src/components/atoms/Box.module.css +++ b/src/components/atoms/Box.module.css @@ -4,7 +4,6 @@ border-radius: var(--border-radius); border: 1px solid var(--border-color); box-shadow: 0 6px 17px 0 var(--box-shadow-color); - overflow: hidden; padding: calc(var(--spacer) / 1.5); } diff --git a/src/components/atoms/Button.module.css b/src/components/atoms/Button.module.css index c83453a4f..d63783b26 100644 --- a/src/components/atoms/Button.module.css +++ b/src/components/atoms/Button.module.css @@ -78,6 +78,7 @@ color: var(--brand-pink); box-shadow: none; cursor: pointer; + min-width: auto; } /* Size Modifiers */ diff --git a/src/components/atoms/ExplorerLink.tsx b/src/components/atoms/ExplorerLink.tsx index 4104e4e86..9f5b23850 100644 --- a/src/components/atoms/ExplorerLink.tsx +++ b/src/components/atoms/ExplorerLink.tsx @@ -4,10 +4,12 @@ import classNames from 'classnames/bind' import { ConfigHelperConfig } from '@oceanprotocol/lib' import { useOcean } from '../../providers/Ocean' import styles from './ExplorerLink.module.css' +import { getOceanConfig } from '../../utils/ocean' const cx = classNames.bind(styles) export default function ExplorerLink({ + networkId, path, children, className @@ -17,22 +19,29 @@ export default function ExplorerLink({ children: ReactNode className?: string }): ReactElement { - const { config } = useOcean() + const { config, ocean } = useOcean() const [url, setUrl] = useState() - + const [oceanConfig, setOceanConfig] = useState() const styleClasses = cx({ link: true, [className]: className }) useEffect(() => { - setUrl((config as ConfigHelperConfig).explorerUri) - }, [config]) + async function initOcean() { + const oceanInitialConfig = getOceanConfig(networkId) + setOceanConfig(oceanInitialConfig) + setUrl(oceanInitialConfig?.explorerUri) + } + if (oceanConfig === undefined) { + initOcean() + } + }, [config, networkId, ocean]) return ( | ChangeEvent ): void + onKeyPress?( + e: + | React.KeyboardEvent + | React.KeyboardEvent + | React.KeyboardEvent + | React.KeyboardEvent + ): void rows?: number multiple?: boolean pattern?: string diff --git a/src/components/atoms/Logo.module.css b/src/components/atoms/Logo.module.css index f86104cce..391bac700 100644 --- a/src/components/atoms/Logo.module.css +++ b/src/components/atoms/Logo.module.css @@ -1,6 +1,6 @@ .logo { - width: 4rem; - height: 4rem; + width: 2.5rem; + height: 2.5rem; margin: 0; } diff --git a/src/components/atoms/NetworkName.module.css b/src/components/atoms/NetworkName.module.css new file mode 100644 index 000000000..6aab11504 --- /dev/null +++ b/src/components/atoms/NetworkName.module.css @@ -0,0 +1,28 @@ +.network { + text-transform: capitalize; + display: inline-flex; + align-items: center; + color: var(--color-secondary); +} + +.icon { + display: inline-block; + width: 1.2em; + height: 1.2em; + fill: currentColor; + margin-right: calc(var(--spacer) / 8); +} + +.minimal { + position: relative; +} + +.minimal .name { + opacity: 0; + width: 0; +} + +.minimal:hover .name { + width: auto; + opacity: 1; +} diff --git a/src/components/atoms/NetworkName.tsx b/src/components/atoms/NetworkName.tsx new file mode 100644 index 000000000..9ddb777bd --- /dev/null +++ b/src/components/atoms/NetworkName.tsx @@ -0,0 +1,48 @@ +import React, { ReactElement } from 'react' +import { ReactComponent as EthIcon } from '../../images/eth.svg' +import { ReactComponent as PolygonIcon } from '../../images/polygon.svg' +import { ReactComponent as MoonbeamIcon } from '../../images/moonbeam.svg' +import { ReactComponent as BscIcon } from '../../images/bsc.svg' +import { getNetworkDataById, getNetworkDisplayName } from '../../utils/web3' +import styles from './NetworkName.module.css' +import useNetworkMetadata from '../../hooks/useNetworkMetadata' + +export function NetworkIcon({ name }: { name: string }): ReactElement { + const IconMapped = name.includes('ETH') + ? EthIcon + : name.includes('Polygon') + ? PolygonIcon + : name.includes('Moon') + ? MoonbeamIcon + : name.includes('BSC') + ? BscIcon + : EthIcon // ETH icon as fallback + + return IconMapped ? : null +} + +export default function NetworkName({ + networkId, + minimal, + className +}: { + networkId: number + minimal?: boolean + className?: string +}): ReactElement { + const { networksList } = useNetworkMetadata() + const networkData = getNetworkDataById(networksList, networkId) + const networkName = getNetworkDisplayName(networkData, networkId) + + return ( + + {' '} + {networkName} + + ) +} diff --git a/src/components/atoms/Price/Conversion.module.css b/src/components/atoms/Price/Conversion.module.css index e7df076f4..0d1849ae8 100644 --- a/src/components/atoms/Price/Conversion.module.css +++ b/src/components/atoms/Price/Conversion.module.css @@ -6,6 +6,9 @@ font-weight: var(--font-weight-base); } +.removeTvlPadding { + padding-left: 0 !important; +} /* fiat currency symbol */ .conversion strong span { font-weight: var(--font-weight-base); diff --git a/src/components/atoms/Price/Conversion.tsx b/src/components/atoms/Price/Conversion.tsx index 558d9d073..7dfe22761 100644 --- a/src/components/atoms/Price/Conversion.tsx +++ b/src/components/atoms/Price/Conversion.tsx @@ -10,11 +10,13 @@ const cx = classNames.bind(styles) export default function Conversion({ price, className, - hideApproximateSymbol + hideApproximateSymbol, + showTVLLabel }: { price: string // expects price in OCEAN, not wei className?: string hideApproximateSymbol?: boolean + showTVLLabel?: boolean }): ReactElement { const { prices } = usePrices() const { currency, locale } = useUserPreferences() @@ -27,6 +29,7 @@ export default function Conversion({ const styleClasses = cx({ conversion: true, + removeTvlPadding: showTVLLabel, [className]: className }) @@ -61,6 +64,7 @@ export default function Conversion({ className={styleClasses} title="Approximation based on current OCEAN spot price on Coingecko" > + {showTVLLabel && 'TVL'} {!hideApproximateSymbol && '≈ '} {' '} {!isFiat && currency} diff --git a/src/components/atoms/Tooltip.module.css b/src/components/atoms/Tooltip.module.css index fb44c7cc9..97bcbda11 100644 --- a/src/components/atoms/Tooltip.module.css +++ b/src/components/atoms/Tooltip.module.css @@ -10,11 +10,11 @@ } .icon { - width: 1rem; - height: 1rem; + width: 1em; + height: 1em; cursor: help; display: inline-block; - margin-bottom: -0.1rem; + margin-bottom: -0.1em; margin-left: calc(var(--spacer) / 6); fill: var(--color-secondary); } diff --git a/src/components/atoms/Tooltip.tsx b/src/components/atoms/Tooltip.tsx index afb2dc489..1719ff0a0 100644 --- a/src/components/atoms/Tooltip.tsx +++ b/src/components/atoms/Tooltip.tsx @@ -28,9 +28,7 @@ export default function Tooltip({ trigger, disabled, className, - placement, - link, - reference + placement }: { content: ReactNode children?: ReactNode @@ -38,8 +36,6 @@ export default function Tooltip({ disabled?: boolean className?: string placement?: Placement - link?: string - reference?: string }): ReactElement { const [props, setSpring] = useSpring(() => animation.from) @@ -76,7 +72,6 @@ export default function Tooltip({
{content} - {link && {reference}}
diff --git a/src/components/molecules/AssetListTitle.tsx b/src/components/molecules/AssetListTitle.tsx index c62bd62f4..9fb6a7535 100644 --- a/src/components/molecules/AssetListTitle.tsx +++ b/src/components/molecules/AssetListTitle.tsx @@ -5,6 +5,7 @@ import React, { ReactElement, useEffect, useState } from 'react' import { getAssetsNames } from '../../utils/aquarius' import styles from './AssetListTitle.module.css' import axios from 'axios' +import { useSiteMetadata } from '../../hooks/useSiteMetadata' export default function AssetListTitle({ ddo, @@ -15,11 +16,11 @@ export default function AssetListTitle({ did?: string title?: string }): ReactElement { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const [assetTitle, setAssetTitle] = useState(title) useEffect(() => { - if (title || !config?.metadataCacheUri) return + if (title || !appConfig.metadataCacheUri) return if (ddo) { const { attributes } = ddo.findServiceByType('metadata') setAssetTitle(attributes.main.name) @@ -29,11 +30,7 @@ export default function AssetListTitle({ const source = axios.CancelToken.source() async function getAssetName() { - const title = await getAssetsNames( - [did], - config.metadataCacheUri, - source.token - ) + const title = await getAssetsNames([did], source.token) setAssetTitle(title[did]) } @@ -42,7 +39,7 @@ export default function AssetListTitle({ return () => { source.cancel() } - }, [assetTitle, config?.metadataCacheUri, ddo, did, title]) + }, [assetTitle, appConfig.metadataCacheUri, ddo, did, title]) return (

diff --git a/src/components/molecules/AssetTeaser.module.css b/src/components/molecules/AssetTeaser.module.css index d18707c42..10ce0ccfc 100644 --- a/src/components/molecules/AssetTeaser.module.css +++ b/src/components/molecules/AssetTeaser.module.css @@ -57,11 +57,6 @@ display: block; } -.date { - font-size: var(--font-size-mini); - margin-top: calc(var(--spacer) / 2); -} - .typeDetails { position: absolute; top: calc(var(--spacer) / 3); @@ -69,3 +64,10 @@ width: auto; font-size: var(--font-size-mini); } + +.network { + font-size: var(--font-size-mini); + position: absolute; + right: calc(var(--spacer) / 3); + bottom: calc(var(--spacer) / 3); +} diff --git a/src/components/molecules/AssetTeaser.tsx b/src/components/molecules/AssetTeaser.tsx index f05bbec8e..c79bfe110 100644 --- a/src/components/molecules/AssetTeaser.tsx +++ b/src/components/molecules/AssetTeaser.tsx @@ -2,12 +2,13 @@ import React from 'react' import { Link } from 'gatsby' import Dotdotdot from 'react-dotdotdot' import Price from '../atoms/Price' -import styles from './AssetTeaser.module.css' import { DDO, BestPrice } from '@oceanprotocol/lib' import removeMarkdown from 'remove-markdown' import Publisher from '../atoms/Publisher' -import Time from '../atoms/Time' import AssetType from '../atoms/AssetType' +import NetworkName from '../atoms/NetworkName' +import { useOcean } from '../../providers/Ocean' +import styles from './AssetTeaser.module.css' declare type AssetTeaserProps = { ddo: DDO @@ -52,9 +53,7 @@ const AssetTeaser: React.FC = ({
-

-

+
diff --git a/src/components/molecules/Bookmarks.tsx b/src/components/molecules/Bookmarks.tsx index c05d9a5e8..58fc09e87 100644 --- a/src/components/molecules/Bookmarks.tsx +++ b/src/components/molecules/Bookmarks.tsx @@ -1,17 +1,21 @@ import { useUserPreferences } from '../../providers/UserPreferences' import React, { ReactElement, useEffect, useState } from 'react' import Table from '../atoms/Table' -import { DDO, Logger, ConfigHelperConfig } from '@oceanprotocol/lib' -import { useOcean } from '../../providers/Ocean' +import { DDO, Logger, BestPrice } from '@oceanprotocol/lib' import Price from '../atoms/Price' import Tooltip from '../atoms/Tooltip' import AssetTitle from './AssetListTitle' -import { queryMetadata } from '../../utils/aquarius' +import { + queryMetadata, + transformChainIdsListToQuery +} from '../../utils/aquarius' +import { getAssetsBestPrices, AssetListPrices } from '../../utils/subgraph' import axios, { CancelToken } from 'axios' +import { useSiteMetadata } from '../../hooks/useSiteMetadata' async function getAssetsBookmarked( bookmarks: string[], - metadataCacheUri: string, + chainIds: number[], cancelToken: CancelToken ) { const searchDids = JSON.stringify(bookmarks) @@ -26,7 +30,9 @@ async function getAssetsBookmarked( offset: 100, query: { query_string: { - query: searchDids, + query: `(${searchDids}) AND (${transformChainIdsListToQuery( + chainIds + )})`, fields: ['dataToken'], default_operator: 'OR' } @@ -35,11 +41,7 @@ async function getAssetsBookmarked( } try { - const result = await queryMetadata( - queryBookmarks, - metadataCacheUri, - cancelToken - ) + const result = await queryMetadata(queryBookmarks, cancelToken) return result } catch (error) { @@ -50,19 +52,19 @@ async function getAssetsBookmarked( const columns = [ { name: 'Data Set', - selector: function getAssetRow(row: DDO) { - const { attributes } = row.findServiceByType('metadata') - return + selector: function getAssetRow(row: AssetListPrices) { + const { attributes } = row.ddo.findServiceByType('metadata') + return }, maxWidth: '45rem', grow: 1 }, { name: 'Datatoken Symbol', - selector: function getAssetRow(row: DDO) { + selector: function getAssetRow(row: AssetListPrices) { return ( - - {row.dataTokenInfo.symbol} + + {row.ddo.dataTokenInfo.symbol} ) }, @@ -70,7 +72,7 @@ const columns = [ }, { name: 'Price', - selector: function getAssetRow(row: DDO) { + selector: function getAssetRow(row: AssetListPrices) { return }, right: true @@ -78,21 +80,20 @@ const columns = [ ] export default function Bookmarks(): ReactElement { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const { bookmarks } = useUserPreferences() - const [pinned, setPinned] = useState() + const [pinned, setPinned] = useState() const [isLoading, setIsLoading] = useState() - - const networkName = (config as ConfigHelperConfig)?.network + const { chainIds } = useUserPreferences() useEffect(() => { - if (!config?.metadataCacheUri || !networkName || bookmarks === {}) return + if (!appConfig.metadataCacheUri || bookmarks === []) return const source = axios.CancelToken.source() async function init() { - if (!bookmarks[networkName]?.length) { + if (!bookmarks?.length) { setPinned([]) return } @@ -101,11 +102,14 @@ export default function Bookmarks(): ReactElement { try { const resultPinned = await getAssetsBookmarked( - bookmarks[networkName], - config.metadataCacheUri, + bookmarks, + chainIds, source.token ) - setPinned(resultPinned?.results) + const pinnedAssets: AssetListPrices[] = await getAssetsBestPrices( + resultPinned?.results + ) + setPinned(pinnedAssets) } catch (error) { Logger.error(error.message) } @@ -117,7 +121,7 @@ export default function Bookmarks(): ReactElement { return () => { source.cancel() } - }, [bookmarks, config.metadataCacheUri, networkName]) + }, [bookmarks, chainIds]) return ( () - const { config } = useOcean() + const { chainId } = useWeb3() function loadFileInfo() { const source = axios.CancelToken.source() + const config = getOceanConfig(chainId || 1) async function validateUrl() { try { setIsLoading(true) const checkedFile = await fileinfo( fileUrl, - config.providerUri, + config?.providerUri, source.token ) checkedFile && helpers.setValue([checkedFile]) @@ -43,7 +45,7 @@ export default function FilesInput(props: InputProps): ReactElement { useEffect(() => { loadFileInfo() - }, [fileUrl, config.providerUri]) + }, [fileUrl]) async function handleButtonClick(e: React.SyntheticEvent, url: string) { // hack so the onBlur-triggered validation does not show, diff --git a/src/components/molecules/MarketStats.module.css b/src/components/molecules/MarketStats.module.css index 51f06eb70..fd872c98d 100644 --- a/src/components/molecules/MarketStats.module.css +++ b/src/components/molecules/MarketStats.module.css @@ -1,18 +1,42 @@ .stats { margin-bottom: calc(var(--spacer) * 2); +} + +/* specificity sledgehammer override without !important */ +.stats, +.stats *, +.statsList * { font-size: var(--font-size-small); - line-height: 2; + color: var(--color-secondary); + margin-left: 0; } -.stats > div > div { - display: inline-block; +.tooltipStats { + margin-bottom: calc(var(--spacer) / 3); + padding-bottom: calc(var(--spacer) / 3); + border-bottom: 1px solid var(--border-color); } -.total { - color: var(--color-secondary) !important; - font-size: var(--font-size-small) !important; +.network { + font-weight: var(--font-weight-bold); } .info { width: 0.85rem; } + +.statsList, +.note { + padding: calc(var(--spacer) / 4); +} + +.statsList { + padding-bottom: 0; +} + +.note { + margin-bottom: 0; + padding-top: 0; + font-size: var(--font-size-mini); + color: var(--color-secondary); +} diff --git a/src/components/molecules/MarketStats.tsx b/src/components/molecules/MarketStats.tsx index b3584dca0..f669e1b39 100644 --- a/src/components/molecules/MarketStats.tsx +++ b/src/components/molecules/MarketStats.tsx @@ -1,9 +1,15 @@ import React, { ReactElement, useEffect, useState } from 'react' -import styles from './MarketStats.module.css' -import { gql, useQuery } from '@apollo/client' +import { gql, OperationContext } from 'urql' import Conversion from '../atoms/Price/Conversion' import PriceUnit from '../atoms/Price/PriceUnit' import Tooltip from '../atoms/Tooltip' +import NetworkName from '../atoms/NetworkName' +import { fetchData, getSubgrahUri } from '../../utils/subgraph' +import { filterNetworksByType } from './UserPreferences/Networks/index' +import { useSiteMetadata } from '../../hooks/useSiteMetadata' +import useNetworkMetadata from '../../hooks/useNetworkMetadata' +import { Logger } from '@oceanprotocol/lib' +import styles from './MarketStats.module.css' const getTotalPoolsValues = gql` query PoolsData { @@ -15,32 +21,160 @@ const getTotalPoolsValues = gql` } ` +interface Value { + [chainId: number]: string +} + +function MarketNetworkStats({ + totalValueLocked, + poolCount, + totalOceanLiquidity +}: { + totalValueLocked: string + poolCount: string + totalOceanLiquidity: string +}): ReactElement { + return ( + <> + {' '} + TVL across{' '} + {poolCount} asset pools that contain{' '} + , + plus datatokens for each pool. + + ) +} + +function MarketNetworkStatsTooltip({ + totalValueLocked, + poolCount, + totalOceanLiquidity, + mainChainIds +}: { + totalValueLocked: Value + poolCount: Value + totalOceanLiquidity: Value + mainChainIds: number[] +}): ReactElement { + return ( + <> +
    + {totalValueLocked && + totalOceanLiquidity && + poolCount && + mainChainIds?.map((chainId, key) => ( +
  • + +
    + {' '} + TVL + {' | '} + {poolCount[chainId] || '0'} pools + {' | '} + +
  • + ))} +
+

+ Counted on-chain from our pool factory. Does not filter out assets in{' '} + + list-purgatory + +

+ + ) +} + export default function MarketStats(): ReactElement { - const [totalValueLocked, setTotalValueLocked] = useState() - const [totalOceanLiquidity, setTotalOceanLiquidity] = useState() - const [poolCount, setPoolCount] = useState() - const { data } = useQuery(getTotalPoolsValues, { pollInterval: 20000 }) + const [totalValueLocked, setTotalValueLocked] = useState() + const [totalOceanLiquidity, setTotalOceanLiquidity] = useState() + const [poolCount, setPoolCount] = useState() + const [totalValueLockedSum, setTotalValueLockedSum] = useState() + const [totalOceanLiquiditySum, setTotalOceanLiquiditySum] = useState() + const [poolCountSum, setPoolCountSum] = useState() + const [mainChainIds, setMainChainIds] = useState() + const { appConfig } = useSiteMetadata() + const { networksList } = useNetworkMetadata() + + async function getMarketStats() { + const mainChainIdsList = await filterNetworksByType( + 'mainnet', + appConfig.chainIdsSupported, + networksList + ) + setMainChainIds(mainChainIdsList) + + let newTotalValueLockedSum = 0 + let newTotalOceanLiquiditySum = 0 + let newPoolCountSum = 0 + + for (const chainId of mainChainIdsList) { + const context: OperationContext = { + url: `${getSubgrahUri( + chainId + )}/subgraphs/name/oceanprotocol/ocean-subgraph`, + requestPolicy: 'network-only' + } + + try { + const response = await fetchData(getTotalPoolsValues, null, context) + if (!response) continue + + const { totalValueLocked, totalOceanLiquidity, finalizedPoolCount } = + response?.data?.poolFactories[0] + + await setTotalValueLocked((prevState) => ({ + ...prevState, + [chainId]: totalValueLocked + })) + await setTotalOceanLiquidity((prevState) => ({ + ...prevState, + [chainId]: totalOceanLiquidity + })) + await setPoolCount((prevState) => ({ + ...prevState, + [chainId]: finalizedPoolCount + })) + + newTotalValueLockedSum += parseInt(totalValueLocked) + newTotalOceanLiquiditySum += parseInt(totalOceanLiquidity) + newPoolCountSum += parseInt(finalizedPoolCount) + } catch (error) { + Logger.error('Error fetchData: ', error.message) + } + } + setTotalValueLockedSum(`${newTotalValueLockedSum}`) + setTotalOceanLiquiditySum(`${newTotalOceanLiquiditySum}`) + setPoolCountSum(`${newPoolCountSum}`) + } useEffect(() => { - if (!data || !data.poolFactories || data.poolFactories.length === 0) return - setTotalValueLocked(data.poolFactories[0].totalValueLocked) - setTotalOceanLiquidity(data.poolFactories[0].totalOceanLiquidity) - setPoolCount(data.poolFactories[0].finalizedPoolCount) - }, [data]) + getMarketStats() + }, []) return (
- {' '} - TVL across{' '} - {poolCount} data set pools that contain{' '} - , - plus datatokens for each pool. - + <> + {' '} + + } + /> +
) } diff --git a/src/components/molecules/Menu.module.css b/src/components/molecules/Menu.module.css index 9f1880304..834d53e1b 100644 --- a/src/components/molecules/Menu.module.css +++ b/src/components/molecules/Menu.module.css @@ -1,32 +1,66 @@ .menu { width: 100%; -} - -.menu > div { + padding: calc(var(--spacer) / 2); display: flex; + align-items: center; justify-content: space-between; - align-items: center; - padding-top: calc(var(--spacer) / 2); - padding-bottom: calc(var(--spacer) / 2); + flex-wrap: wrap; } -.logoUnit { +.logo { + order: 1; + white-space: nowrap; display: flex; align-items: center; } -.logoUnit svg { - margin-left: -0.5rem; - margin-right: 0.5rem; +.navigation { + order: 3; + margin-top: calc(var(--spacer) / 2); + text-align: center; + border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); + margin-left: -1rem; + margin-right: -1rem; + width: calc(100% + 2rem); +} + +.actions { + order: 2; + display: flex; } .title { display: none; } -@media screen and (min-width: 40rem) { +@media screen and (min-width: 42rem) { + .menu { + justify-content: start; + } + + .navigation { + order: 2; + width: auto; + margin: 0; + text-align: left; + border: none; + } + + .actions { + order: 3; + margin-left: auto; + } +} + +@media screen and (min-width: 55rem) { + .menu { + padding: var(--spacer); + } + .title { margin: 0; + margin-right: var(--spacer); display: block; color: var(--color-secondary); font-size: var(--font-size-h4); @@ -38,6 +72,7 @@ overflow-y: hidden; overflow-x: auto; -webkit-overflow-scrolling: touch; + -ms-overflow-style: none; } .navigation::-webkit-scrollbar, @@ -48,19 +83,13 @@ .navigation li { display: inline-block; vertical-align: middle; - margin-left: var(--spacer); + margin-right: calc(var(--spacer) / 3); + margin-left: calc(var(--spacer) / 3); } -@media screen and (min-width: 60rem) { - .navigation li { - margin-left: calc(var(--spacer) * 2); - } -} - -.navigation button, .link { display: block; - padding: calc(var(--spacer) / 2); + padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2); text-transform: uppercase; color: var(--color-secondary); font-weight: var(--font-weight-bold); @@ -69,16 +98,11 @@ z-index: 1; } -.navigation button { - text-transform: none; - padding-top: calc(var(--spacer) / 4); - padding-bottom: calc(var(--spacer) / 4); -} - +.actions, .link:hover, .link:focus, .link:active { - color: var(--brand-grey); + color: var(--font-color-text); } .link[aria-current], @@ -90,3 +114,11 @@ .link:last-child { padding-right: 0; } + +.logo svg { + margin-right: calc(var(--spacer) / 3); +} + +.actions button { + text-transform: none; +} diff --git a/src/components/molecules/Menu.tsx b/src/components/molecules/Menu.tsx index f257b5121..67fa5c4db 100644 --- a/src/components/molecules/Menu.tsx +++ b/src/components/molecules/Menu.tsx @@ -4,10 +4,11 @@ import { useLocation } from '@reach/router' import loadable from '@loadable/component' import styles from './Menu.module.css' import { useSiteMetadata } from '../../hooks/useSiteMetadata' -import Container from '../atoms/Container' import UserPreferences from './UserPreferences' import Badge from '../atoms/Badge' import Logo from '../atoms/Logo' +import Networks from './UserPreferences/Networks' +import SearchBar from './SearchBar' const Wallet = loadable(() => import('./Wallet')) @@ -36,28 +37,27 @@ export default function Menu(): ReactElement { return ( ) } diff --git a/src/components/molecules/MetadataPreview.module.css b/src/components/molecules/MetadataPreview.module.css index e14eab2a9..c440d7ef1 100644 --- a/src/components/molecules/MetadataPreview.module.css +++ b/src/components/molecules/MetadataPreview.module.css @@ -33,7 +33,6 @@ } .datatoken { - margin-top: calc(var(--spacer) / 8); margin-bottom: 0; color: var(--color-secondary); } diff --git a/src/components/molecules/MetadataPreview.tsx b/src/components/molecules/MetadataPreview.tsx index a1a85c126..976803a54 100644 --- a/src/components/molecules/MetadataPreview.tsx +++ b/src/components/molecules/MetadataPreview.tsx @@ -3,7 +3,6 @@ import { File as FileMetadata } from '@oceanprotocol/lib/dist/node/ddo/interface import Markdown from '../atoms/Markdown' import Tags from '../atoms/Tags' import MetaItem from '../organisms/AssetContent/MetaItem' -import styles from './MetadataPreview.module.css' import File from '../atoms/File' import { MetadataPublishFormDataset, @@ -11,6 +10,11 @@ import { } from '../../@types/MetaData' import Button from '../atoms/Button' import { transformTags } from '../../utils/metadata' +import NetworkName from '../atoms/NetworkName' +import { useWeb3 } from '../../providers/Web3' +import styles from './MetadataPreview.module.css' +import Web3Feedback from './Web3Feedback' +import { useAsset } from '../../providers/Asset' function Description({ description }: { description: string }) { const [fullDescription, setFullDescription] = useState(false) @@ -92,10 +96,14 @@ export function MetadataPreview({ }: { values: Partial }): ReactElement { + const { networkId } = useWeb3() + const { isAssetNetwork } = useAsset() + return (

Preview

+ {networkId && } {values.name &&

{values.name}

} {values.dataTokenOptions?.name && (

+ {isAssetNetwork === false && ( + + )}

) } @@ -130,10 +141,13 @@ export function MetadataAlgorithmPreview({ }: { values: Partial }): ReactElement { + const { networkId } = useWeb3() + return (

Preview

+ {networkId && } {values.name &&

{values.name}

} {values.dataTokenOptions?.name && (

(announcement.main) - const [action, setAction] = useState() - - const addCustomNetworkAction = { - name: 'Add custom network', - handleAction: () => addCustomNetwork(web3Provider, networkMatic) - } - const switchToPolygonAction = { - name: 'Switch to Polygon', - handleAction: async () => { - const config = getOceanConfig('polygon') - await connect(config) - } - } - const switchToEthAction = { - name: 'Switch to ETH', - handleAction: async () => { - const config = getOceanConfig('mainnet') - await connect(config) - } - } - - function setBannerForMatic() { - setText(announcement.polygon) - setAction(undefined) - } - - useEffect(() => { - if (!web3ProviderInfo || (!web3Provider && !config)) return - - switch (web3ProviderInfo.name) { - case 'Web3': - if (config.networkId !== 137) { - setText(announcement.main) - setAction(switchToPolygonAction) - } else { - setText(announcement.polygon) - setAction(switchToEthAction) - } - break - case 'MetaMask': - if (config.networkId === 137) { - setBannerForMatic() - } else { - setText(announcement.main) - setAction(addCustomNetworkAction) - } - break - default: - if (config.networkId === 137) { - setBannerForMatic() - } else { - setText(announcement.main) - setAction(undefined) - } - } - }, [web3Provider, web3ProviderInfo, config, announcement]) - - return -} diff --git a/src/components/molecules/PoolTransactions.tsx b/src/components/molecules/PoolTransactions.tsx deleted file mode 100644 index 9da914dc3..000000000 --- a/src/components/molecules/PoolTransactions.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import { useOcean } from '../../providers/Ocean' -import React, { ReactElement, useEffect, useState } from 'react' -import ExplorerLink from '../atoms/ExplorerLink' -import Time from '../atoms/Time' -import Table from '../atoms/Table' -import AssetTitle from './AssetListTitle' -import styles from './PoolTransactions.module.css' -import { useUserPreferences } from '../../providers/UserPreferences' -import { Ocean } from '@oceanprotocol/lib' -import { formatPrice } from '../atoms/Price/PriceUnit' -import { gql, useQuery } from '@apollo/client' -import { - TransactionHistory, - TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions -} from '../../@types/apollo/TransactionHistory' - -import web3 from 'web3' -import { useWeb3 } from '../../providers/Web3' - -const txHistoryQueryByPool = gql` - query TransactionHistoryByPool($user: String, $pool: String) { - poolTransactions( - orderBy: timestamp - orderDirection: desc - where: { userAddress: $user, poolAddress: $pool } - first: 1000 - ) { - tx - event - timestamp - poolAddress { - datatokenAddress - } - tokens { - value - type - tokenAddress - } - } - } -` -const txHistoryQuery = gql` - query TransactionHistory($user: String) { - poolTransactions( - orderBy: timestamp - orderDirection: desc - where: { userAddress: $user } - first: 1000 - ) { - tx - event - timestamp - poolAddress { - datatokenAddress - } - tokens { - value - type - tokenAddress - } - } - } -` -async function getSymbol(ocean: Ocean, tokenAddress: string) { - const symbol = - ocean.pool.oceanAddress.toLowerCase() === tokenAddress.toLowerCase() - ? 'OCEAN' - : await ocean.datatokens.getSymbol(tokenAddress) - - return symbol -} - -async function getTitle( - ocean: Ocean, - row: TransactionHistoryPoolTransactions, - locale: string -) { - let title = '' - - switch (row.event) { - case 'swap': { - const inToken = row.tokens.filter((x) => x.type === 'in')[0] - const inTokenSymbol = await getSymbol(ocean, inToken.tokenAddress) - const outToken = row.tokens.filter((x) => x.type === 'out')[0] - const outTokenSymbol = await getSymbol(ocean, outToken.tokenAddress) - title += `Swap ${formatPrice( - Math.abs(inToken.value).toString(), - locale - )}${inTokenSymbol} for ${formatPrice( - Math.abs(outToken.value).toString(), - locale - )}${outTokenSymbol}` - break - } - case 'setup': { - const firstToken = row.tokens.filter( - (x) => - x.tokenAddress.toLowerCase() === ocean.pool.oceanAddress.toLowerCase() - )[0] - const firstTokenSymbol = await getSymbol(ocean, firstToken.tokenAddress) - const secondToken = row.tokens.filter( - (x) => - x.tokenAddress.toLowerCase() !== ocean.pool.oceanAddress.toLowerCase() - )[0] - const secondTokenSymbol = await getSymbol(ocean, secondToken.tokenAddress) - title += `Create pool with ${formatPrice( - Math.abs(firstToken.value).toString(), - locale - )}${firstTokenSymbol} and ${formatPrice( - Math.abs(secondToken.value).toString(), - locale - )}${secondTokenSymbol}` - break - } - case 'join': - case 'exit': { - for (let i = 0; i < row.tokens.length; i++) { - const tokenSymbol = await getSymbol(ocean, row.tokens[i].tokenAddress) - if (i > 0) title += '\n' - title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice( - Math.abs(row.tokens[i].value).toString(), - locale - )}${tokenSymbol}` - } - break - } - } - - return title -} - -function Title({ row }: { row: TransactionHistoryPoolTransactions }) { - const { networkId } = useWeb3() - const { ocean } = useOcean() - const [title, setTitle] = useState() - const { locale } = useUserPreferences() - - useEffect(() => { - if (!ocean || !locale || !row) return - - async function init() { - const title = await getTitle(ocean, row, locale) - setTitle(title) - } - init() - }, [ocean, row, locale]) - - return title ? ( - - {title} - - ) : null -} - -const columns = [ - { - name: 'Title', - selector: function getTitleRow(row: TransactionHistoryPoolTransactions) { - return - } - }, - { - name: 'Data Set', - selector: function getAssetRow(row: TransactionHistoryPoolTransactions) { - const did = web3.utils - .toChecksumAddress(row.poolAddress.datatokenAddress) - .replace('0x', 'did:op:') - - return <AssetTitle did={did} /> - } - }, - { - name: 'Time', - selector: function getTimeRow(row: TransactionHistoryPoolTransactions) { - return ( - <Time - className={styles.time} - date={row.timestamp.toString()} - relative - isUnix - /> - ) - }, - maxWidth: '10rem' - } -] - -// hack! if we use a function to omit one field this will display a strange refresh to the enduser for each row -const columnsMinimal = [columns[0], columns[2]] - -export default function PoolTransactions({ - poolAddress, - minimal -}: { - poolAddress?: string - minimal?: boolean -}): ReactElement { - const { accountId } = useWeb3() - const [logs, setLogs] = useState<TransactionHistoryPoolTransactions[]>() - - const { data, loading } = useQuery<TransactionHistory>( - poolAddress ? txHistoryQueryByPool : txHistoryQuery, - { - variables: { - user: accountId?.toLowerCase(), - pool: poolAddress?.toLowerCase() - }, - pollInterval: 20000 - } - ) - - useEffect(() => { - if (!data) return - setLogs(data.poolTransactions) - }, [data, loading]) - - return ( - <Table - columns={minimal ? columnsMinimal : columns} - data={logs} - isLoading={loading} - noTableHead={minimal} - dense={minimal} - pagination={minimal ? logs?.length >= 4 : logs?.length >= 9} - paginationPerPage={minimal ? 5 : 10} - emptyMessage="Your pool transactions will show up here" - /> - ) -} diff --git a/src/components/molecules/PoolTransactions/Title.module.css b/src/components/molecules/PoolTransactions/Title.module.css new file mode 100644 index 000000000..bb3bb7d69 --- /dev/null +++ b/src/components/molecules/PoolTransactions/Title.module.css @@ -0,0 +1,3 @@ +.titleText { + white-space: pre; +} diff --git a/src/components/molecules/PoolTransactions/Title.tsx b/src/components/molecules/PoolTransactions/Title.tsx new file mode 100644 index 000000000..5305c34f7 --- /dev/null +++ b/src/components/molecules/PoolTransactions/Title.tsx @@ -0,0 +1,88 @@ +import React, { useState, useEffect, ReactElement } from 'react' +import { Datatoken, PoolTransaction } from '.' +import { useUserPreferences } from '../../../providers/UserPreferences' +import ExplorerLink from '../../atoms/ExplorerLink' +import { formatPrice } from '../../atoms/Price/PriceUnit' +import styles from './Title.module.css' + +function getSymbol(tokenId: Datatoken) { + const symbol = tokenId === null ? 'OCEAN' : tokenId.symbol + return symbol +} + +async function getTitle(row: PoolTransaction, locale: string) { + let title = '' + switch (row.event) { + case 'swap': { + const inToken = row.tokens.filter((x) => x.type === 'in')[0] + const inTokenSymbol = getSymbol(inToken.poolToken.tokenId) + const outToken = row.tokens.filter((x) => x.type === 'out')[0] + const outTokenSymbol = getSymbol(outToken.poolToken.tokenId) + title += `Swap ${formatPrice( + Math.abs(inToken.value).toString(), + locale + )}${inTokenSymbol} for ${formatPrice( + Math.abs(outToken.value).toString(), + locale + )}${outTokenSymbol}` + + break + } + case 'setup': { + const firstToken = row.tokens.filter( + (x) => + x.tokenAddress.toLowerCase() !== + row.poolAddress.datatokenAddress.toLowerCase() + )[0] + const firstTokenSymbol = await getSymbol(firstToken.poolToken.tokenId) + const secondToken = row.tokens.filter( + (x) => + x.tokenAddress.toLowerCase() === + row.poolAddress.datatokenAddress.toLowerCase() + )[0] + const secondTokenSymbol = await getSymbol(secondToken.poolToken.tokenId) + title += `Create pool with ${formatPrice( + Math.abs(firstToken.value).toString(), + locale + )}${firstTokenSymbol} and ${formatPrice( + Math.abs(secondToken.value).toString(), + locale + )}${secondTokenSymbol}` + break + } + case 'join': + case 'exit': { + for (let i = 0; i < row.tokens.length; i++) { + const tokenSymbol = await getSymbol(row.tokens[i].poolToken.tokenId) + if (i > 0) title += '\n' + title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice( + Math.abs(row.tokens[i].value).toString(), + locale + )}${tokenSymbol}` + } + break + } + } + + return title +} + +export default function Title({ row }: { row: PoolTransaction }): ReactElement { + const [title, setTitle] = useState<string>() + const { locale } = useUserPreferences() + + useEffect(() => { + if (!locale || !row) return + async function init() { + const title = await getTitle(row, locale) + setTitle(title) + } + init() + }, [row, locale]) + + return title ? ( + <ExplorerLink networkId={row.networkId} path={`/tx/${row.tx}`}> + <span className={styles.titleText}>{title}</span> + </ExplorerLink> + ) : null +} diff --git a/src/components/molecules/PoolTransactions.module.css b/src/components/molecules/PoolTransactions/index.module.css similarity index 54% rename from src/components/molecules/PoolTransactions.module.css rename to src/components/molecules/PoolTransactions/index.module.css index 12bb497f5..197568bba 100644 --- a/src/components/molecules/PoolTransactions.module.css +++ b/src/components/molecules/PoolTransactions/index.module.css @@ -1,7 +1,3 @@ .time { color: var(--color-secondary); } - -.titleText { - white-space: pre; -} diff --git a/src/components/molecules/PoolTransactions/index.tsx b/src/components/molecules/PoolTransactions/index.tsx new file mode 100644 index 000000000..ca0d174a2 --- /dev/null +++ b/src/components/molecules/PoolTransactions/index.tsx @@ -0,0 +1,232 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import Time from '../../atoms/Time' +import Table from '../../atoms/Table' +import AssetTitle from '../AssetListTitle' +import { useUserPreferences } from '../../../providers/UserPreferences' +import { gql } from 'urql' +import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/apollo/TransactionHistory' +import web3 from 'web3' +import { useWeb3 } from '../../../providers/Web3' +import { fetchDataForMultipleChains } from '../../../utils/subgraph' +import { useSiteMetadata } from '../../../hooks/useSiteMetadata' +import NetworkName from '../../atoms/NetworkName' +import { retrieveDDO } from '../../../utils/aquarius' +import axios from 'axios' +import Title from './Title' +import styles from './index.module.css' + +const REFETCH_INTERVAL = 20000 + +const txHistoryQueryByPool = gql` + query TransactionHistoryByPool($user: String, $pool: String) { + poolTransactions( + orderBy: timestamp + orderDirection: desc + where: { userAddress: $user, poolAddress: $pool } + first: 1000 + ) { + tokens { + poolToken { + tokenId { + symbol + } + } + } + tx + event + timestamp + poolAddress { + datatokenAddress + } + tokens { + value + type + tokenAddress + } + } + } +` +const txHistoryQuery = gql` + query TransactionHistory($user: String) { + poolTransactions( + orderBy: timestamp + orderDirection: desc + where: { userAddress: $user } + first: 1000 + ) { + tokens { + poolToken { + tokenId { + symbol + } + } + } + tx + event + timestamp + poolAddress { + datatokenAddress + } + tokens { + value + type + tokenAddress + } + } + } +` + +export interface Datatoken { + symbol: string +} + +export interface PoolTransaction extends TransactionHistoryPoolTransactions { + networkId: number +} + +const columns = [ + { + name: 'Title', + selector: function getTitleRow(row: PoolTransaction) { + return <Title row={row} /> + } + }, + { + name: 'Data Set', + selector: function getAssetRow(row: PoolTransaction) { + const did = web3.utils + .toChecksumAddress(row.poolAddress.datatokenAddress) + .replace('0x', 'did:op:') + + return <AssetTitle did={did} /> + } + }, + { + name: 'Network', + selector: function getNetwork(row: PoolTransaction) { + return <NetworkName networkId={row.networkId} /> + }, + maxWidth: '12rem' + }, + { + name: 'Time', + selector: function getTimeRow(row: PoolTransaction) { + return ( + <Time + className={styles.time} + date={row.timestamp.toString()} + relative + isUnix + /> + ) + }, + maxWidth: '10rem' + } +] + +// hack! if we use a function to omit one field this will display a strange refresh to the enduser for each row +const columnsMinimal = [columns[0], columns[3]] + +export default function PoolTransactions({ + poolAddress, + minimal +}: { + poolAddress?: string + minimal?: boolean +}): ReactElement { + const { accountId } = useWeb3() + const [logs, setLogs] = useState<PoolTransaction[]>() + const [isLoading, setIsLoading] = useState<boolean>(false) + const { chainIds } = useUserPreferences() + const { appConfig } = useSiteMetadata() + const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>() + const [data, setData] = useState<PoolTransaction[]>() + + async function fetchPoolTransactionData() { + const variables = { user: accountId?.toLowerCase() } + const transactions: PoolTransaction[] = [] + const result = await fetchDataForMultipleChains( + poolAddress ? txHistoryQueryByPool : txHistoryQuery, + variables, + chainIds + ) + for (let i = 0; i < result.length; i++) { + result[i].poolTransactions.forEach((poolTransaction: PoolTransaction) => { + transactions.push(poolTransaction) + }) + } + + if (JSON.stringify(data) !== JSON.stringify(transactions)) { + setData(transactions) + } + } + + function refetchPoolTransactions() { + if (!dataFetchInterval) { + setDataFetchInterval( + setInterval(function () { + fetchPoolTransactionData() + }, REFETCH_INTERVAL) + ) + } + } + + useEffect(() => { + return () => { + clearInterval(dataFetchInterval) + } + }, [dataFetchInterval]) + + useEffect(() => { + if (!appConfig.metadataCacheUri) return + + async function getTransactions() { + const poolTransactions: PoolTransaction[] = [] + const source = axios.CancelToken.source() + try { + setIsLoading(true) + + if (!data) { + await fetchPoolTransactionData() + return + } + const poolTransactionsData = data.map((obj) => ({ ...obj })) + + for (let i = 0; i < poolTransactionsData.length; i++) { + const did = web3.utils + .toChecksumAddress( + poolTransactionsData[i].poolAddress.datatokenAddress + ) + .replace('0x', 'did:op:') + const ddo = await retrieveDDO(did, source.token) + poolTransactionsData[i].networkId = ddo.chainId + poolTransactions.push(poolTransactionsData[i]) + } + const sortedTransactions = poolTransactions.sort( + (a, b) => b.timestamp - a.timestamp + ) + setLogs(sortedTransactions) + refetchPoolTransactions() + } catch (error) { + console.error('Error fetching pool transactions: ', error.message) + } finally { + setIsLoading(false) + } + } + getTransactions() + }, [accountId, chainIds, appConfig.metadataCacheUri, poolAddress, data]) + + return accountId ? ( + <Table + columns={minimal ? columnsMinimal : columns} + data={logs} + isLoading={isLoading} + noTableHead={minimal} + dense={minimal} + pagination={minimal ? logs?.length >= 4 : logs?.length >= 9} + paginationPerPage={minimal ? 5 : 10} + /> + ) : ( + <div>Please connect your Web3 wallet.</div> + ) +} diff --git a/src/components/molecules/SearchBar.module.css b/src/components/molecules/SearchBar.module.css index c40741f52..cbd81c652 100644 --- a/src/components/molecules/SearchBar.module.css +++ b/src/components/molecules/SearchBar.module.css @@ -1,17 +1,72 @@ -.form { - margin-bottom: var(--spacer); +.search { + display: flex; + position: relative; +} + +.button { + color: var(--color-secondary); + cursor: pointer; + background: var(--background-content); + border: none; + box-shadow: none; + padding: 0; + position: absolute; + padding: calc(var(--spacer) / 4); width: 100%; - max-width: 30rem; + right: 1px; + left: 1px; + top: 1px; + bottom: 1px; + z-index: -1; } -.form > div > div { +.button:hover, +.button:focus { + color: var(--font-color-text); +} + +.input { + background-color: transparent; + height: 36px; margin: 0; + outline: 0; + padding-right: var(--spacer); + width: 0; + transition: none; } -.form label { - display: none; -} - -.form input { +.input:focus { + width: calc(100% - var(--spacer)); background-color: var(--background-content); + position: fixed; + left: calc(var(--spacer) / 2); + right: 0; + z-index: 2; +} + +@media screen and (min-width: 78rem) { + .input, + .input:focus { + width: auto; + position: relative; + left: initial; + right: initial; + } + + .button { + width: auto; + left: auto; + background: none; + } + + .input:focus + .button { + z-index: 3; + } +} + +.searchIcon { + fill: currentColor; + transition: 0.2s ease-out; + width: var(--font-size-h5); + height: var(--font-size-h5); } diff --git a/src/components/molecules/SearchBar.stories.tsx b/src/components/molecules/SearchBar.stories.tsx index 31c512a99..d116b926e 100644 --- a/src/components/molecules/SearchBar.stories.tsx +++ b/src/components/molecules/SearchBar.stories.tsx @@ -10,5 +10,3 @@ export default { export const Normal = () => <SearchBar /> export const WithInitialValue = () => <SearchBar initialValue="Water" /> - -export const WithFilters = () => <SearchBar filters /> diff --git a/src/components/molecules/SearchBar.tsx b/src/components/molecules/SearchBar.tsx index 1dfb2c4d7..b28c5d986 100644 --- a/src/components/molecules/SearchBar.tsx +++ b/src/components/molecules/SearchBar.tsx @@ -1,27 +1,51 @@ -import React, { useState, ChangeEvent, FormEvent, ReactElement } from 'react' +import React, { + useState, + useEffect, + ChangeEvent, + FormEvent, + KeyboardEvent, + ReactElement +} from 'react' import { navigate } from 'gatsby' -import styles from './SearchBar.module.css' -import Button from '../atoms/Button' -import Input from '../atoms/Input' -import InputGroup from '../atoms/Input/InputGroup' +import queryString from 'query-string' import { addExistingParamsToUrl } from '../templates/Search/utils' +import { ReactComponent as SearchIcon } from '../../images/search.svg' +import InputElement from '../atoms/Input/InputElement' +import styles from './SearchBar.module.css' + +async function emptySearch() { + const searchParams = new URLSearchParams(window.location.href) + const text = searchParams.get('text') + if (text !== ('' || undefined || null)) { + const url = await addExistingParamsToUrl(location, [ + 'text', + 'owner', + 'tags' + ]) + navigate(`${url}&text=%20`) + } +} export default function SearchBar({ placeholder, - initialValue, - filters, - size + initialValue }: { placeholder?: string initialValue?: string - filters?: boolean - size?: 'small' | 'large' }): ReactElement { - let [value, setValue] = useState(initialValue || '') + const [value, setValue] = useState(initialValue || '') + const parsed = queryString.parse(location.search) + const { text, owner } = parsed + + useEffect(() => { + ;(text || owner) && setValue((text || owner) as string) + }, [text, owner]) async function startSearch(e: FormEvent<HTMLButtonElement>) { e.preventDefault() - if (value === '') value = ' ' + + if (value === '') setValue(' ') + const urlEncodedValue = encodeURIComponent(value) const url = await addExistingParamsToUrl(location, [ 'text', @@ -31,46 +55,38 @@ export default function SearchBar({ navigate(`${url}&text=${urlEncodedValue}`) } - async function emptySearch() { - const searchParams = new URLSearchParams(window.location.href) - const text = searchParams.get('text') - if (text !== ('' || undefined || null)) { - const url = await addExistingParamsToUrl(location, [ - 'text', - 'owner', - 'tags' - ]) - navigate(`${url}&text=%20`) - } - } - function handleChange(e: ChangeEvent<HTMLInputElement>) { setValue(e.target.value) e.target.value === '' && emptySearch() } - return ( - <form className={styles.form}> - <InputGroup> - <Input - type="search" - name="search" - placeholder={placeholder || 'What are you looking for?'} - value={value} - onChange={handleChange} - required - size={size} - /> - <Button - onClick={async (e: FormEvent<HTMLButtonElement>) => - await startSearch(e) - } - > - Search - </Button> - </InputGroup> + async function handleKeyPress(e: KeyboardEvent<HTMLInputElement>) { + if (e.key === 'Enter') { + await startSearch(e) + } + } - {filters && <fieldset className={styles.filters}>Type, Price</fieldset>} + async function handleButtonClick(e: FormEvent<HTMLButtonElement>) { + e.preventDefault() + await startSearch(e) + } + + return ( + <form className={styles.search}> + <InputElement + type="search" + name="search" + placeholder={placeholder || 'Search...'} + value={value} + onChange={handleChange} + required + size="small" + className={styles.input} + onKeyPress={handleKeyPress} + /> + <button onClick={handleButtonClick} className={styles.button}> + <SearchIcon className={styles.searchIcon} /> + </button> </form> ) } diff --git a/src/components/molecules/UserPreferences/Chain.module.css b/src/components/molecules/UserPreferences/Chain.module.css deleted file mode 100644 index 568fc76cb..000000000 --- a/src/components/molecules/UserPreferences/Chain.module.css +++ /dev/null @@ -1,30 +0,0 @@ -.buttons { - composes: buttons from './Appearance.module.css'; -} - -.button { - composes: button from './Appearance.module.css'; -} - -.button span { - display: block; - font-size: var(--font-size-small); - font-family: var(--font-family-base); - font-weight: var(--font-weight-base); - margin-top: calc(var(--spacer) / 10); -} - -.selected { - composes: selected from './Appearance.module.css'; -} - -.chains div[class*='boxSelectionsWrapper'] { - display: grid; - gap: calc(var(--spacer) / 4); - grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr)); - padding-bottom: calc(var(--spacer) / 8); -} - -.chains label[class*='boxSelection'] { - padding: calc(var(--spacer) / 3) calc(var(--spacer) / 4) !important; -} diff --git a/src/components/molecules/UserPreferences/Chain.tsx b/src/components/molecules/UserPreferences/Chain.tsx deleted file mode 100644 index e9d77d029..000000000 --- a/src/components/molecules/UserPreferences/Chain.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ConfigHelperConfig } from '@oceanprotocol/lib' -import React, { ReactElement, ChangeEvent } from 'react' -import { useOcean } from '../../../providers/Ocean' -import { useWeb3 } from '../../../providers/Web3' -import { getOceanConfig } from '../../../utils/ocean' -import FormHelp from '../../atoms/Input/Help' -import Label from '../../atoms/Input/Label' -import BoxSelection, { BoxSelectionOption } from '../FormFields/BoxSelection' -import styles from './Chain.module.css' - -export default function Chain(): ReactElement { - const { web3 } = useWeb3() - const { config, connect } = useOcean() - - async function connectOcean(event: ChangeEvent<HTMLInputElement>) { - const config = getOceanConfig(event.target.value) - await connect(config) - } - - function isNetworkSelected(oceanConfig: string) { - return (config as ConfigHelperConfig).network === oceanConfig - } - - const options: BoxSelectionOption[] = [ - { - name: 'mainnet', - checked: isNetworkSelected('mainnet'), - title: 'ETH', - text: 'Mainnet' - }, - { - name: 'polygon', - checked: isNetworkSelected('polygon'), - title: 'Polygon/Matic', - text: 'Mainnet' - }, - { - name: 'bsc', - checked: isNetworkSelected('bsc'), - title: 'BSC', - text: 'Mainnet' - }, - { - name: 'moonbeamalpha', - checked: isNetworkSelected('moonbeamalpha'), - title: 'Moonbase Alpha', - text: 'Testnet' - } - ] - - // TODO: to fully solve https://github.com/oceanprotocol/market/issues/432 - // there are more considerations for users with a wallet connected (wallet network vs. setting network). - // For now, only show the setting for non-wallet users. - return !web3 ? ( - <li className={styles.chains}> - <Label htmlFor="">Chain</Label> - <BoxSelection - options={options} - name="chain" - handleChange={connectOcean} - /> - <FormHelp>Switch the data source for the interface.</FormHelp> - </li> - ) : null -} diff --git a/src/components/molecules/UserPreferences/Debug.tsx b/src/components/molecules/UserPreferences/Debug.tsx index 5aef8a52e..ce1630e07 100644 --- a/src/components/molecules/UserPreferences/Debug.tsx +++ b/src/components/molecules/UserPreferences/Debug.tsx @@ -1,23 +1,21 @@ import React, { ReactElement } from 'react' import { useUserPreferences } from '../../../providers/UserPreferences' -import FormHelp from '../../atoms/Input/Help' -import InputElement from '../../atoms/Input/InputElement' +import Input from '../../atoms/Input' export default function Debug(): ReactElement { const { debug, setDebug } = useUserPreferences() return ( <li> - <InputElement + <Input + label="Debug" + help="Show geeky information in some places, and in your console." name="debug" type="checkbox" - options={['Debug Mode']} + options={['Activate Debug Mode']} defaultChecked={debug === true} onChange={() => setDebug(!debug)} /> - <FormHelp> - Show geeky information in some places, and in your console. - </FormHelp> </li> ) } diff --git a/src/components/molecules/UserPreferences/Networks/NetworkItem.module.css b/src/components/molecules/UserPreferences/Networks/NetworkItem.module.css new file mode 100644 index 000000000..9f26deeb6 --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/NetworkItem.module.css @@ -0,0 +1,31 @@ +.radioWrap { + composes: radioWrap from '../../../atoms/Input/InputElement.module.css'; + padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3); + border-bottom: 1px solid var(--border-color); +} + +.radioWrap:last-child { + border-bottom: none; +} + +.input { + composes: checkbox from '../../../atoms/Input/InputElement.module.css'; + vertical-align: baseline; + margin-right: calc(var(--spacer) / 3); + margin-top: 0; +} + +.radioLabel { + composes: radioLabel from '../../../atoms/Input/InputElement.module.css'; + font-weight: var(--font-weight-base); + display: flex; + align-items: center; + margin: 0; + padding: 0; + width: 100%; +} + +.input:checked + span { + color: var(--font-color-text); + font-weight: var(--font-weight-bold); +} diff --git a/src/components/molecules/UserPreferences/Networks/NetworkItem.tsx b/src/components/molecules/UserPreferences/Networks/NetworkItem.tsx new file mode 100644 index 000000000..a2fb88ec6 --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/NetworkItem.tsx @@ -0,0 +1,42 @@ +import React, { ChangeEvent, ReactElement } from 'react' +import { useUserPreferences } from '../../../../providers/UserPreferences' +import { removeItemFromArray } from '../../../../utils' +import NetworkName from '../../../atoms/NetworkName' +import styles from './NetworkItem.module.css' + +export default function NetworkItem({ + chainId +}: { + chainId: number +}): ReactElement { + const { chainIds, setChainIds } = useUserPreferences() + + function handleNetworkChanged(e: ChangeEvent<HTMLInputElement>) { + const { value } = e.target + + // storing all chainId everywhere as a number so convert from here + const valueAsNumber = Number(value) + + const newChainIds = chainIds.includes(valueAsNumber) + ? [...removeItemFromArray(chainIds, valueAsNumber)] + : [...chainIds, valueAsNumber] + setChainIds(newChainIds) + } + + return ( + <div className={styles.radioWrap} key={chainId}> + <label className={styles.radioLabel} htmlFor={`opt-${chainId}`}> + <input + className={styles.input} + id={`opt-${chainId}`} + type="checkbox" + name="chainIds" + value={chainId} + onChange={handleNetworkChanged} + defaultChecked={chainIds.includes(chainId)} + /> + <NetworkName key={chainId} networkId={chainId} /> + </label> + </div> + ) +} diff --git a/src/components/molecules/UserPreferences/Networks/NetworksList.module.css b/src/components/molecules/UserPreferences/Networks/NetworksList.module.css new file mode 100644 index 000000000..d20cbdd08 --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/NetworksList.module.css @@ -0,0 +1,12 @@ +.titleGroup { + font-size: var(--font-size-small); + margin-bottom: calc(var(--spacer) / 6); + margin-top: calc(var(--spacer) / 2); + color: var(--color-secondary); +} + +.networks { + composes: box from '../../../atoms/Box.module.css'; + box-shadow: none; + padding: 0; +} diff --git a/src/components/molecules/UserPreferences/Networks/NetworksList.tsx b/src/components/molecules/UserPreferences/Networks/NetworksList.tsx new file mode 100644 index 000000000..2cfa80d9f --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/NetworksList.tsx @@ -0,0 +1,22 @@ +import React, { ReactElement } from 'react' +import NetworkItem from './NetworkItem' +import styles from './NetworksList.module.css' + +export default function NetworksList({ + title, + networks +}: { + title: string + networks: number[] +}): ReactElement { + const content = networks.map((chainId) => ( + <NetworkItem key={chainId} chainId={chainId} /> + )) + + return ( + <> + <h4 className={styles.titleGroup}>{title}</h4> + <div className={styles.networks}>{content}</div> + </> + ) +} diff --git a/src/components/molecules/UserPreferences/Networks/index.module.css b/src/components/molecules/UserPreferences/Networks/index.module.css new file mode 100644 index 000000000..e4a5e90cb --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/index.module.css @@ -0,0 +1,22 @@ +.networks { + margin-right: calc(var(--spacer) / 3); + position: relative; + overflow: hidden; +} + +.chainsSelected { + text-align: center; + position: absolute; + bottom: -8px; + left: 0; + width: 100%; +} + +.chainsSelectedIndicator { + width: 4px; + height: 4px; + border-radius: 50%; + margin: 0 1px; + display: inline-block; + background-color: var(--color-primary); +} diff --git a/src/components/molecules/UserPreferences/Networks/index.tsx b/src/components/molecules/UserPreferences/Networks/index.tsx new file mode 100644 index 000000000..28cc83463 --- /dev/null +++ b/src/components/molecules/UserPreferences/Networks/index.tsx @@ -0,0 +1,76 @@ +import React, { ReactElement } from 'react' +import Label from '../../../atoms/Input/Label' +import { useSiteMetadata } from '../../../../hooks/useSiteMetadata' +import FormHelp from '../../../atoms/Input/Help' +import { EthereumListsChain, getNetworkDataById } from '../../../../utils/web3' +import Tooltip from '../../../atoms/Tooltip' +import { ReactComponent as Caret } from '../../../../images/caret.svg' +import { ReactComponent as Network } from '../../../../images/network.svg' +import NetworksList from './NetworksList' +import stylesIndex from '../index.module.css' +import styles from './index.module.css' +import useNetworkMetadata from '../../../../hooks/useNetworkMetadata' +import { useUserPreferences } from '../../../../providers/UserPreferences' + +export function filterNetworksByType( + type: 'mainnet' | 'testnet', + chainIds: number[], + networksList: { node: EthereumListsChain }[] +) { + const finalNetworks = chainIds.filter((chainId: number) => { + const networkData = getNetworkDataById(networksList, chainId) + + // HEADS UP! Only networkData.network === 'mainnet' is consistent + // while not every test network in the network data has 'testnet' + // in its place. So for the 'testnet' case filter for all non-'mainnet'. + return type === 'mainnet' + ? networkData.network === type + : networkData.network !== 'mainnet' + }) + return finalNetworks +} + +export default function Networks(): ReactElement { + const { networksList } = useNetworkMetadata() + const { appConfig } = useSiteMetadata() + const { chainIds } = useUserPreferences() + + const networksMain = filterNetworksByType( + 'mainnet', + appConfig.chainIdsSupported, + networksList + ) + + const networksTest = filterNetworksByType( + 'testnet', + appConfig.chainIdsSupported, + networksList + ) + + return ( + <Tooltip + content={ + <ul className={stylesIndex.preferencesDetails}> + <li> + <Label htmlFor="chains">Networks</Label> + <FormHelp>Switch the data source for the interface.</FormHelp> + + <NetworksList title="Main" networks={networksMain} /> + <NetworksList title="Test" networks={networksTest} /> + </li> + </ul> + } + trigger="click focus" + className={`${stylesIndex.preferences} ${styles.networks}`} + > + <Network aria-label="Networks" className={stylesIndex.icon} /> + <Caret aria-hidden="true" className={stylesIndex.caret} /> + + <div className={styles.chainsSelected}> + {chainIds.map((chainId) => ( + <span className={styles.chainsSelectedIndicator} key={chainId} /> + ))} + </div> + </Tooltip> + ) +} diff --git a/src/components/molecules/UserPreferences/index.module.css b/src/components/molecules/UserPreferences/index.module.css index 1adc1a58c..8246b1d3c 100644 --- a/src/components/molecules/UserPreferences/index.module.css +++ b/src/components/molecules/UserPreferences/index.module.css @@ -1,7 +1,11 @@ .preferences { - padding: calc(var(--spacer) / 5) calc(var(--spacer) / 4) - calc(var(--spacer) / 5) 0; + padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3); cursor: pointer; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + margin-left: calc(var(--spacer) / 3); + background-color: var(--background-content); + white-space: nowrap; } .preferences svg { @@ -13,19 +17,28 @@ transform: rotate(180deg); } -.preferences svg:last-child { - width: 1em; - height: 1em; +.caret, +svg.caret { + width: var(--font-size-small); + height: var(--font-size-small); fill: var(--border-color); margin-left: calc(var(--spacer) / 4); transition: transform 0.2s ease-out; + display: none; +} + +@media screen and (min-width: 42rem) { + .caret, + svg.caret { + display: inline-block; + } } .icon { fill: var(--brand-grey-light); transition: 0.2s ease-out; - width: 1.2em; - height: 1.2em; + width: 1em; + height: 1em; } .preferences:hover .icon, @@ -40,16 +53,16 @@ max-width: 20rem; } -.preferencesDetails li > div, -.preferencesDetails li p { - margin: 0; +.preferencesDetails > li > div, +.preferencesDetails p:last-child { + margin-bottom: 0; } .preferencesDetails li p { margin-top: calc(var(--spacer) / 8); } -.preferencesDetails li { +.preferencesDetails > li { padding-top: calc(var(--spacer) / 3); padding-bottom: calc(var(--spacer) / 3); } diff --git a/src/components/molecules/UserPreferences/index.tsx b/src/components/molecules/UserPreferences/index.tsx index 3c07f5326..445e7f43f 100644 --- a/src/components/molecules/UserPreferences/index.tsx +++ b/src/components/molecules/UserPreferences/index.tsx @@ -8,10 +8,9 @@ import { ReactComponent as Caret } from '../../../images/caret.svg' import useDarkMode from 'use-dark-mode' import Appearance from './Appearance' import { darkModeConfig } from '../../../../app.config' -import Chain from './Chain' export default function UserPreferences(): ReactElement { - // Calling this here because <Theme /> is not mounted on first load + // Calling this here because <Style /> is not mounted on first load const darkMode = useDarkMode(false, darkModeConfig) return ( @@ -20,7 +19,6 @@ export default function UserPreferences(): ReactElement { <ul className={styles.preferencesDetails}> <Currency /> <Appearance darkMode={darkMode} /> - <Chain /> <Debug /> </ul> } @@ -28,7 +26,7 @@ export default function UserPreferences(): ReactElement { className={styles.preferences} > <Cog aria-label="Preferences" className={styles.icon} /> - <Caret aria-hidden="true" /> + <Caret aria-hidden="true" className={styles.caret} /> </Tooltip> ) } diff --git a/src/components/molecules/Wallet/Account.module.css b/src/components/molecules/Wallet/Account.module.css index 260255310..0a4d0d6be 100644 --- a/src/components/molecules/Wallet/Account.module.css +++ b/src/components/molecules/Wallet/Account.module.css @@ -5,14 +5,15 @@ text-transform: uppercase; border: 1px solid var(--border-color); border-radius: var(--border-radius); - padding: calc(var(--spacer) / 4); + padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3); white-space: nowrap; background: var(--background-content); margin: 0; transition: border 0.2s ease-out; cursor: pointer; - min-width: 190px; height: 100%; + display: flex; + align-items: center; } .button, @@ -30,6 +31,16 @@ color: var(--color-primary); } +.button.initial span { + display: none; +} + +@media screen and (min-width: 42rem) { + .button.initial span { + display: inline; + } +} + .blockies { width: var(--font-size-large); height: var(--font-size-large); @@ -37,15 +48,25 @@ overflow: hidden; display: inline-block; vertical-align: middle; - margin-right: calc(var(--spacer) / 6); } .address { + display: none; text-transform: none; border-right: 1px solid var(--border-color); padding-right: calc(var(--spacer) / 3); } +@media screen and (min-width: 60rem) { + .address { + display: inline-block; + } + + .blockies { + margin-right: calc(var(--spacer) / 6); + } +} + .button svg { width: 1em; height: 1em; @@ -65,3 +86,15 @@ position: relative; top: 1px; } + +.caret, +svg.caret { + display: none; +} + +@media screen and (min-width: 42rem) { + .caret, + svg.caret { + display: inline-block; + } +} diff --git a/src/components/molecules/Wallet/Account.tsx b/src/components/molecules/Wallet/Account.tsx index 5eef23d33..7b3be33fc 100644 --- a/src/components/molecules/Wallet/Account.tsx +++ b/src/components/molecules/Wallet/Account.tsx @@ -35,7 +35,7 @@ const Account = React.forwardRef((props, ref: any) => { return !accountId && web3Modal?.cachedProvider ? ( // Improve user experience for cached provider when connecting takes some time <button className={styles.button} onClick={(e) => e.preventDefault()}> - <Loader message="Reconnecting wallet..." /> + <Loader message="Reconnecting..." /> </button> ) : accountId ? ( <button @@ -48,7 +48,7 @@ const Account = React.forwardRef((props, ref: any) => { <span className={styles.address} title={accountId}> {accountTruncate(accountId)} </span> - <Caret aria-hidden="true" /> + <Caret aria-hidden="true" className={styles.caret} /> </button> ) : ( <button @@ -58,7 +58,7 @@ const Account = React.forwardRef((props, ref: any) => { // the Tippy to show in this state. ref={ref} > - Connect Wallet + Connect <span>Wallet</span> </button> ) }) diff --git a/src/components/molecules/Wallet/Details.tsx b/src/components/molecules/Wallet/Details.tsx index 6858f92fb..e1c6db4c7 100644 --- a/src/components/molecules/Wallet/Details.tsx +++ b/src/components/molecules/Wallet/Details.tsx @@ -1,14 +1,14 @@ import React, { ReactElement, useEffect, useState } from 'react' import { formatCurrency } from '@coingecko/cryptoformat' -import { useOcean } from '../../../providers/Ocean' import { useUserPreferences } from '../../../providers/UserPreferences' import Button from '../../atoms/Button' import AddToken from '../../atoms/AddToken' import Conversion from '../../atoms/Price/Conversion' import { useWeb3 } from '../../../providers/Web3' -import Web3Feedback from './Feedback' +import Web3Feedback from '../Web3Feedback' import styles from './Details.module.css' +import { getOceanConfig } from '../../../utils/ocean' export default function Details(): ReactElement { const { @@ -17,20 +17,34 @@ export default function Details(): ReactElement { web3Modal, connect, logout, + networkData, networkId, - networkData + balance } = useWeb3() - const { balance, config } = useOcean() const { locale } = useUserPreferences() const [mainCurrency, setMainCurrency] = useState<string>() + const [oceanTokenMetadata, setOceanTokenMetadata] = + useState<{ + address: string + symbol: string + }>() // const [portisNetwork, setPortisNetwork] = useState<string>() useEffect(() => { + if (!networkId) return + const symbol = networkId === 2021000 ? 'GX' : networkData?.nativeCurrency.symbol - setMainCurrency(symbol) + + const oceanConfig = getOceanConfig(networkId) + + oceanConfig && + setOceanTokenMetadata({ + address: oceanConfig.oceanTokenAddress, + symbol: oceanConfig.oceanTokenSymbol + }) }, [networkData, networkId]) // Handle network change for Portis @@ -49,7 +63,7 @@ export default function Details(): ReactElement { {Object.entries(balance).map(([key, value]) => ( <li className={styles.balance} key={key}> <span className={styles.symbol}> - {key === 'eth' ? mainCurrency : config.oceanTokenSymbol} + {key === 'eth' ? mainCurrency : oceanTokenMetadata?.symbol} </span>{' '} {formatCurrency(Number(value), '', locale, false, { significantFigures: 4 @@ -76,8 +90,8 @@ export default function Details(): ReactElement { )} */} {web3ProviderInfo?.name === 'MetaMask' && ( <AddToken - address={config.oceanTokenAddress} - symbol={config.oceanTokenSymbol} + address={oceanTokenMetadata?.address} + symbol={oceanTokenMetadata?.symbol} logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png" className={styles.addToken} /> diff --git a/src/components/molecules/Wallet/Feedback.tsx b/src/components/molecules/Wallet/Feedback.tsx deleted file mode 100644 index ae69249ff..000000000 --- a/src/components/molecules/Wallet/Feedback.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { ReactElement } from 'react' -import { useOcean } from '../../../providers/Ocean' -import Status from '../../atoms/Status' -import styles from './Feedback.module.css' - -export declare type Web3Error = { - status: 'error' | 'warning' | 'success' - title: string - message?: string -} - -export default function Web3Feedback({ - isBalanceSufficient -}: { - isBalanceSufficient?: boolean -}): ReactElement { - const { account, ocean } = useOcean() - const showFeedback = !account || !ocean || isBalanceSufficient === false - - const state = !account - ? 'error' - : account && isBalanceSufficient - ? 'success' - : 'warning' - - const title = !account - ? 'No account connected' - : !ocean - ? 'Error connecting to Ocean' - : account - ? isBalanceSufficient === false - ? 'Insufficient balance' - : 'Connected to Ocean' - : 'Something went wrong' - - const message = !account - ? 'Please connect your Web3 wallet.' - : !ocean - ? 'Please try again.' - : isBalanceSufficient === false - ? 'You do not have enough OCEAN in your wallet to purchase this asset.' - : 'Something went wrong.' - - return showFeedback ? ( - <section className={styles.feedback}> - <Status state={state} aria-hidden /> - <h3 className={styles.title}>{title}</h3> - {message && <p className={styles.error}>{message}</p>} - </section> - ) : null -} diff --git a/src/components/molecules/Wallet/Network.module.css b/src/components/molecules/Wallet/Network.module.css index 8e676b984..decb67a88 100644 --- a/src/components/molecules/Wallet/Network.module.css +++ b/src/components/molecules/Wallet/Network.module.css @@ -3,7 +3,7 @@ border-right: none; border-top-left-radius: var(--border-radius); border-bottom-left-radius: var(--border-radius); - padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2); + padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3); white-space: nowrap; margin-right: -3px; display: inline-flex; @@ -12,8 +12,6 @@ .name { font-size: var(--font-size-small); - display: inline-block; - text-transform: capitalize; } .badge { diff --git a/src/components/molecules/Wallet/Network.tsx b/src/components/molecules/Wallet/Network.tsx index 675d31f14..bd3299c40 100644 --- a/src/components/molecules/Wallet/Network.tsx +++ b/src/components/molecules/Wallet/Network.tsx @@ -1,41 +1,36 @@ import React, { useState, useEffect, ReactElement } from 'react' -import { useOcean } from '../../../providers/Ocean' import Status from '../../atoms/Status' -import { ConfigHelper, ConfigHelperConfig } from '@oceanprotocol/lib' -import styles from './Network.module.css' import Badge from '../../atoms/Badge' import Tooltip from '../../atoms/Tooltip' import { useWeb3 } from '../../../providers/Web3' +import NetworkName from '../../atoms/NetworkName' +import styles from './Network.module.css' +import { getOceanConfig } from '../../../utils/ocean' export default function Network(): ReactElement { - const { networkId, networkDisplayName, isTestnet } = useWeb3() - const { config } = useOcean() - const networkIdConfig = (config as ConfigHelperConfig).networkId + const { networkId, isTestnet } = useWeb3() - const [isEthMainnet, setIsEthMainnet] = useState<boolean>() - const [isSupportedNetwork, setIsSupportedNetwork] = useState<boolean>() + const [isSupportedOceanNetwork, setIsSupportedOceanNetwork] = + useState<boolean>() useEffect(() => { - // take network from user when present, - // otherwise use the default configured one of app - const network = networkId || networkIdConfig - const isEthMainnet = network === 1 - setIsEthMainnet(isEthMainnet) + // take network from user when present + const network = networkId || 1 // Check networkId against ocean.js ConfigHelper configs // to figure out if network is supported. - const isSupportedNetwork = Boolean(new ConfigHelper().getConfig(network)) - setIsSupportedNetwork(isSupportedNetwork) - }, [networkId, networkIdConfig]) + const isSupportedOceanNetwork = Boolean(getOceanConfig(network)) + setIsSupportedOceanNetwork(isSupportedOceanNetwork) + }, [networkId]) - return !isEthMainnet && networkDisplayName ? ( + return networkId ? ( <div className={styles.network}> - {!isSupportedNetwork && ( + {!isSupportedOceanNetwork && ( <Tooltip content="No Ocean Protocol contracts are deployed to this network."> <Status state="error" className={styles.warning} /> </Tooltip> )} - <span className={styles.name}>{networkDisplayName}</span> + <NetworkName className={styles.name} networkId={networkId} minimal /> {isTestnet && <Badge label="Test" className={styles.badge} />} </div> ) : null diff --git a/src/components/molecules/Wallet/index.module.css b/src/components/molecules/Wallet/index.module.css index 68f94fd0b..2993716bf 100644 --- a/src/components/molecules/Wallet/index.module.css +++ b/src/components/molecules/Wallet/index.module.css @@ -1,3 +1,4 @@ .wallet { display: flex; + align-self: stretch; } diff --git a/src/components/molecules/Wallet/index.stories.tsx b/src/components/molecules/Wallet/index.stories.tsx index 46e29819b..45ea79970 100644 --- a/src/components/molecules/Wallet/index.stories.tsx +++ b/src/components/molecules/Wallet/index.stories.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Web3Feedback from './Feedback' +import Web3Feedback from '../Web3Feedback' import web3Mock from '../../../../tests/unit/__mocks__/web3' import { Center } from '../../../../.storybook/helpers' diff --git a/src/components/molecules/Wallet/index.tsx b/src/components/molecules/Wallet/index.tsx index d89f01828..e417015f7 100644 --- a/src/components/molecules/Wallet/index.tsx +++ b/src/components/molecules/Wallet/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useState } from 'react' +import React, { ReactElement } from 'react' import Account from './Account' import Details from './Details' import Tooltip from '../../atoms/Tooltip' diff --git a/src/components/molecules/WalletNetworkSwitcher.module.css b/src/components/molecules/WalletNetworkSwitcher.module.css new file mode 100644 index 000000000..24e7cb047 --- /dev/null +++ b/src/components/molecules/WalletNetworkSwitcher.module.css @@ -0,0 +1,5 @@ +.text { + color: var(--color-secondary); + margin-top: calc(var(--spacer) / 4); + margin-bottom: calc(var(--spacer) / 2); +} diff --git a/src/components/molecules/WalletNetworkSwitcher.tsx b/src/components/molecules/WalletNetworkSwitcher.tsx new file mode 100644 index 000000000..d3563873d --- /dev/null +++ b/src/components/molecules/WalletNetworkSwitcher.tsx @@ -0,0 +1,51 @@ +import React, { ReactElement } from 'react' +import { useWeb3 } from '../../providers/Web3' +import { + addCustomNetwork, + getNetworkConfigObject, + getNetworkDisplayName, + getNetworkDataById +} from '../../utils/web3' +import Button from '../atoms/Button' +import styles from './WalletNetworkSwitcher.module.css' +import useNetworkMetadata from '../../hooks/useNetworkMetadata' +import { getOceanConfig } from '../../utils/ocean' +import { useAsset } from '../../providers/Asset' + +export default function WalletNetworkSwitcher(): ReactElement { + const { networkId, web3Provider } = useWeb3() + const { ddo } = useAsset() + const oceanConfig = getOceanConfig(ddo.chainId) + const { networksList } = useNetworkMetadata() + const ddoNetworkData = getNetworkDataById(networksList, ddo.chainId) + const walletNetworkData = getNetworkDataById(networksList, networkId) + + const ddoNetworkName = ( + <strong>{getNetworkDisplayName(ddoNetworkData, ddo.chainId)}</strong> + ) + const walletNetworkName = ( + <strong>{getNetworkDisplayName(walletNetworkData, networkId)}</strong> + ) + + async function switchWalletNetwork() { + const networkNode = networksList.find( + (data) => data.node.chainId === ddo.chainId + ).node + const network = { ...networkNode, providerUri: oceanConfig.providerUri } + const networkConfig = getNetworkConfigObject(network) + addCustomNetwork(web3Provider, networkConfig) + } + + return ( + <> + <p className={styles.text}> + This asset is published on {ddoNetworkName} but your wallet is connected + to {walletNetworkName}. Connect to {ddoNetworkName} to interact with + this asset. + </p> + <Button size="small" onClick={() => switchWalletNetwork()}> + Switch to {ddoNetworkName} + </Button> + </> + ) +} diff --git a/src/components/molecules/Wallet/Feedback.module.css b/src/components/molecules/Web3Feedback.module.css similarity index 78% rename from src/components/molecules/Wallet/Feedback.module.css rename to src/components/molecules/Web3Feedback.module.css index ce11665c6..e4ddf87fd 100644 --- a/src/components/molecules/Wallet/Feedback.module.css +++ b/src/components/molecules/Web3Feedback.module.css @@ -2,8 +2,7 @@ font-size: var(--font-size-small); padding-left: var(--spacer); padding-top: calc(var(--spacer) / 1.5); - margin-top: var(--spacer); - border-top: 1px solid var(--border-color); + margin-top: calc(var(--spacer) / 2); position: relative; width: 100%; } @@ -11,7 +10,7 @@ .feedback i { position: absolute; left: 0; - top: calc(var(--spacer) / 1.5); + top: calc(var(--spacer) / 1.45); } .title { diff --git a/src/components/molecules/Web3Feedback.tsx b/src/components/molecules/Web3Feedback.tsx new file mode 100644 index 000000000..486c2a682 --- /dev/null +++ b/src/components/molecules/Web3Feedback.tsx @@ -0,0 +1,70 @@ +import React, { ReactElement, useEffect } from 'react' +import { useWeb3 } from '../../providers/Web3' +import Status from '../atoms/Status' +import styles from './Web3Feedback.module.css' +import WalletNetworkSwitcher from './WalletNetworkSwitcher' +import { useGraphSyncStatus } from '../../hooks/useGraphSyncStatus' + +export declare type Web3Error = { + status: 'error' | 'warning' | 'success' + title: string + message?: string +} + +export default function Web3Feedback({ + isBalanceSufficient, + isAssetNetwork +}: { + isBalanceSufficient?: boolean + isAssetNetwork?: boolean +}): ReactElement { + const { accountId } = useWeb3() + const { isGraphSynced, blockGraph, blockHead } = useGraphSyncStatus() + const showFeedback = + !accountId || + // !ocean || + isBalanceSufficient === false || + isAssetNetwork === false || + isGraphSynced === false + + const state = + !accountId || !isGraphSynced + ? 'error' + : accountId && isBalanceSufficient && isAssetNetwork + ? 'success' + : 'warning' + + const title = !accountId + ? 'No account connected' + : // : !ocean + // ? 'Error connecting to Ocean' + accountId && isAssetNetwork === false + ? 'Not connected to asset network' + : isGraphSynced === false + ? `Data out of sync` + : accountId + ? isBalanceSufficient === false + ? 'Insufficient balance' + : 'Connected to Ocean' + : 'Something went wrong' + + const message = !accountId + ? 'Please connect your Web3 wallet.' + : isBalanceSufficient === false + ? 'You do not have enough OCEAN in your wallet to purchase this asset.' + : isGraphSynced === false + ? `The data for this network has only synced to Ethereum block ${blockGraph} (out of ${blockHead}). Transactions may fail! Please check back soon` + : 'Something went wrong.' + + return showFeedback ? ( + <section className={styles.feedback}> + <Status state={state} aria-hidden /> + <h3 className={styles.title}>{title}</h3> + {isAssetNetwork === false ? ( + <WalletNetworkSwitcher /> + ) : ( + message && <p className={styles.error}>{message}</p> + )} + </section> + ) : null +} diff --git a/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx b/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx index d2ef3d5e5..4faa36a4a 100644 --- a/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/organisms/AssetActions/Compute/FormComputeDataset.tsx @@ -94,7 +94,7 @@ export default function FormStartCompute({ const { isValid, values }: FormikContextType<{ algorithm: string }> = useFormikContext() - const { price, ddo } = useAsset() + const { price, ddo, isAssetNetwork } = useAsset() const [totalPrice, setTotalPrice] = useState(price?.value) const { accountId } = useWeb3() const { ocean } = useOcean() @@ -175,7 +175,10 @@ export default function FormStartCompute({ <ButtonBuy action="compute" disabled={ - isComputeButtonDisabled || !isValid || algorithmConsumableStatus > 0 + isComputeButtonDisabled || + !isValid || + !isAssetNetwork || + algorithmConsumableStatus > 0 } hasPreviousOrder={hasPreviousOrder} hasDatatoken={hasDatatoken} diff --git a/src/components/organisms/AssetActions/Compute/index.tsx b/src/components/organisms/AssetActions/Compute/index.tsx index f0550acb6..ec32d2acf 100644 --- a/src/components/organisms/AssetActions/Compute/index.tsx +++ b/src/components/organisms/AssetActions/Compute/index.tsx @@ -10,7 +10,6 @@ import { toast } from 'react-toastify' import Price from '../../../atoms/Price' import File from '../../../atoms/File' import Alert from '../../../atoms/Alert' -import Web3Feedback from '../../../molecules/Wallet/Feedback' import { useSiteMetadata } from '../../../../hooks/useSiteMetadata' import { useOcean } from '../../../../providers/Ocean' import { useWeb3 } from '../../../../providers/Web3' @@ -39,6 +38,7 @@ import { secondsToString } from '../../../../utils/metadata' import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute' import { getPreviousOrders, getPrice } from '../../../../utils/subgraph' +import { chainIds } from '../../../../../app.config' const SuccessAction = () => ( <Button style="text" to="/history?defaultTab=ComputeJobs" size="small"> @@ -63,8 +63,8 @@ export default function Compute({ }): ReactElement { const { appConfig } = useSiteMetadata() const { accountId } = useWeb3() - const { ocean, account, config } = useOcean() - const { price, type, ddo } = useAsset() + const { ocean, account } = useOcean() + const { price, type, ddo, isAssetNetwork } = useAsset() const { buyDT, pricingError, pricingStepText } = usePricing() const [isJobStarting, setIsJobStarting] = useState(false) const [error, setError] = useState<string>() @@ -142,7 +142,8 @@ export default function Compute({ } function getQuerryString( - trustedAlgorithmList: publisherTrustedAlgorithm[] + trustedAlgorithmList: publisherTrustedAlgorithm[], + chainId?: number ): SearchQuery { let algoQuerry = '' trustedAlgorithmList.forEach((trusteAlgo) => { @@ -157,7 +158,7 @@ export default function Compute({ offset: 500, query: { query_string: { - query: `${algorithmQuery} service.attributes.main.type:algorithm -isInPurgatory:true` + query: `${algorithmQuery} service.attributes.main.type:algorithm AND chainId:${chainId} -isInPurgatory:true` } }, sort: { created: -1 } @@ -180,9 +181,9 @@ export default function Compute({ } else { const gueryResults = await queryMetadata( getQuerryString( - computeService.attributes.main.privacy.publisherTrustedAlgorithms + computeService.attributes.main.privacy.publisherTrustedAlgorithms, + ddo.chainId ), - config.metadataCacheUri, source.token ) setDdoAlgorithmList(gueryResults.results) @@ -190,7 +191,6 @@ export default function Compute({ algorithmSelectionList = await transformDDOToAssetSelection( datasetComputeService?.serviceEndpoint, gueryResults.results, - config.metadataCacheUri, [] ) } @@ -472,9 +472,6 @@ export default function Compute({ action={<SuccessAction />} /> )} - {type !== 'algorithm' && ( - <Web3Feedback isBalanceSufficient={isBalanceSufficient} /> - )} </footer> </> ) diff --git a/src/components/organisms/AssetActions/Consume.module.css b/src/components/organisms/AssetActions/Consume.module.css index c095bcfcf..d6dc45629 100644 --- a/src/components/organisms/AssetActions/Consume.module.css +++ b/src/components/organisms/AssetActions/Consume.module.css @@ -6,7 +6,6 @@ .info { display: flex; width: auto; - padding: 0 calc(var(--spacer)) 0 calc(var(--spacer)); } .filewrapper { @@ -15,4 +14,5 @@ .feedback { width: 100%; + margin-top: calc(var(--spacer)); } diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 195faf0f6..ae3ba97af 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -3,12 +3,9 @@ import { toast } from 'react-toastify' import { File as FileMetadata, DDO, BestPrice } from '@oceanprotocol/lib' import File from '../../atoms/File' import Price from '../../atoms/Price' -import Web3Feedback from '../../molecules/Wallet/Feedback' -import styles from './Consume.module.css' import { useSiteMetadata } from '../../../hooks/useSiteMetadata' import { useAsset } from '../../../providers/Asset' -import { secondsToString } from '../../../utils/metadata' -import { gql, useQuery } from '@apollo/client' +import { gql, useQuery } from 'urql' import { OrdersData } from '../../../@types/apollo/OrdersData' import BigNumber from 'bignumber.js' import { useOcean } from '../../../providers/Ocean' @@ -16,7 +13,9 @@ import { useWeb3 } from '../../../providers/Web3' import { usePricing } from '../../../hooks/usePricing' import { useConsume } from '../../../hooks/useConsume' import ButtonBuy from '../../atoms/ButtonBuy' +import { secondsToString } from '../../../utils/metadata' import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute' +import styles from './Consume.module.css' const previousOrderQuery = gql` query PreviousOrder($id: String!, $account: String!) { @@ -54,7 +53,7 @@ export default function Consume({ const { appConfig } = useSiteMetadata() const [hasPreviousOrder, setHasPreviousOrder] = useState(false) const [previousOrderId, setPreviousOrderId] = useState<string>() - const { isInPurgatory, price, type } = useAsset() + const { isInPurgatory, price, type, isAssetNetwork } = useAsset() const { buyDT, pricingStepText, pricingError, pricingIsLoading } = usePricing() const { consumeStepText, consume, consumeError, isLoading } = useConsume() @@ -63,13 +62,15 @@ export default function Consume({ const [maxDt, setMaxDT] = useState<number>(1) const [isConsumablePrice, setIsConsumablePrice] = useState(true) const [assetTimeout, setAssetTimeout] = useState('') - const { data } = useQuery<OrdersData>(previousOrderQuery, { + const [result] = useQuery<OrdersData>({ + query: previousOrderQuery, variables: { id: ddo.dataToken?.toLowerCase(), account: accountId?.toLowerCase() - }, - pollInterval: 5000 + } + // pollInterval: 5000 }) + const { data } = result async function checkMaxAvaialableTokens(price: BestPrice) { if (!ocean || !price) return @@ -122,6 +123,7 @@ export default function Consume({ !isConsumable || ((!ocean || !isBalanceSufficient || + !isAssetNetwork || typeof consumeStepText !== 'undefined' || pricingIsLoading || (!hasPreviousOrder && !hasDatatoken && !(maxDt >= 1)) || @@ -133,6 +135,7 @@ export default function Consume({ ocean, hasPreviousOrder, isBalanceSufficient, + isAssetNetwork, consumeStepText, pricingIsLoading, isConsumablePrice, @@ -198,9 +201,6 @@ export default function Consume({ {type === 'algorithm' && ( <AlgorithmDatasetsListForCompute algorithmDid={ddo.id} dataset={ddo} /> )} - <footer className={styles.feedback}> - <Web3Feedback isBalanceSufficient={isBalanceSufficient} /> - </footer> </aside> ) } diff --git a/src/components/organisms/AssetActions/Edit/EditAdvancedSettings.tsx b/src/components/organisms/AssetActions/Edit/EditAdvancedSettings.tsx index 9534797f6..2854c0f41 100644 --- a/src/components/organisms/AssetActions/Edit/EditAdvancedSettings.tsx +++ b/src/components/organisms/AssetActions/Edit/EditAdvancedSettings.tsx @@ -20,6 +20,7 @@ import { setMinterToDispenser, setMinterToPublisher } from '../../../../utils/freePrice' +import Web3Feedback from '../../../molecules/Web3Feedback' const contentQuery = graphql` query EditAvanceSettingsQuery { @@ -72,7 +73,7 @@ export default function EditAdvancedSettings({ const { debug } = useUserPreferences() const { accountId } = useWeb3() const { ocean } = useOcean() - const { metadata, ddo, refreshDdo, price } = useAsset() + const { isAssetNetwork, ddo, refreshDdo, price } = useAsset() const [success, setSuccess] = useState<string>() const [error, setError] = useState<string>() const { appConfig } = useSiteMetadata() @@ -168,7 +169,7 @@ export default function EditAdvancedSettings({ setShowEdit={setShowEdit} /> </article> - + <Web3Feedback isAssetNetwork={isAssetNetwork} /> {debug === true && ( <div className={styles.grid}> <DebugEditCredential diff --git a/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx index 1c65aaa70..c9b5976c7 100644 --- a/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx +++ b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx @@ -20,6 +20,7 @@ import { setMinterToDispenser, setMinterToPublisher } from '../../../../utils/freePrice' +import Web3Feedback from '../../../molecules/Web3Feedback' const contentQuery = graphql` query EditComputeDataQuery { @@ -66,7 +67,7 @@ export default function EditComputeDataset({ const { debug } = useUserPreferences() const { ocean } = useOcean() const { accountId } = useWeb3() - const { ddo, refreshDdo, price } = useAsset() + const { ddo, price, isAssetNetwork, refreshDdo } = useAsset() const [success, setSuccess] = useState<string>() const [error, setError] = useState<string>() @@ -169,7 +170,7 @@ export default function EditComputeDataset({ setShowEdit={setShowEdit} /> </article> - + <Web3Feedback isAssetNetwork={isAssetNetwork} /> {debug === true && ( <div className={styles.grid}> <DebugEditCompute values={values} ddo={ddo} /> diff --git a/src/components/organisms/AssetActions/Edit/FormActions.module.css b/src/components/organisms/AssetActions/Edit/FormActions.module.css new file mode 100644 index 000000000..67c39f9f6 --- /dev/null +++ b/src/components/organisms/AssetActions/Edit/FormActions.module.css @@ -0,0 +1,20 @@ +.actions { + margin-left: -2rem; + margin-right: -2rem; + border-top: 1px solid var(--border-color); + padding: calc(var(--spacer) / 2) var(--spacer) 0; + display: flex; + justify-content: center; +} + +@media (min-width: 40rem) { + .actions { + padding-top: var(--spacer); + } +} + +.actions a, +.actions button { + margin-left: calc(var(--spacer) / 2); + margin-right: calc(var(--spacer) / 2); +} diff --git a/src/components/organisms/AssetActions/Edit/FormActions.tsx b/src/components/organisms/AssetActions/Edit/FormActions.tsx new file mode 100644 index 000000000..d674578b9 --- /dev/null +++ b/src/components/organisms/AssetActions/Edit/FormActions.tsx @@ -0,0 +1,37 @@ +import { FormikContextType, useFormikContext } from 'formik' +import React, { ReactElement } from 'react' +import { AdvancedSettingsForm } from '../../../../models/FormEditCredential' +import { useAsset } from '../../../../providers/Asset' +import { useOcean } from '../../../../providers/Ocean' +import { useWeb3 } from '../../../../providers/Web3' +import Button from '../../../atoms/Button' +import styles from './FormActions.module.css' + +export default function FormActions({ + setShowEdit, + handleClick +}: { + setShowEdit: (show: boolean) => void + handleClick?: () => void +}): ReactElement { + const { accountId } = useWeb3() + const { ocean } = useOcean() + const { isAssetNetwork } = useAsset() + const { isValid }: FormikContextType<Partial<AdvancedSettingsForm>> = + useFormikContext() + + return ( + <footer className={styles.actions}> + <Button + style="primary" + disabled={!ocean || !accountId || !isValid || !isAssetNetwork} + onClick={handleClick} + > + Submit + </Button> + <Button style="text" onClick={() => setShowEdit(false)}> + Cancel + </Button> + </footer> + ) +} diff --git a/src/components/organisms/AssetActions/Edit/FormAdvancedSettings.tsx b/src/components/organisms/AssetActions/Edit/FormAdvancedSettings.tsx index a67186abf..02f261808 100644 --- a/src/components/organisms/AssetActions/Edit/FormAdvancedSettings.tsx +++ b/src/components/organisms/AssetActions/Edit/FormAdvancedSettings.tsx @@ -1,12 +1,10 @@ import React, { ChangeEvent, ReactElement } from 'react' import styles from './FormEditMetadata.module.css' import { Field, Form, FormikContextType, useFormikContext } from 'formik' -import Button from '../../../atoms/Button' import Input from '../../../atoms/Input' import { FormFieldProps } from '../../../../@types/Form' -import { useOcean } from '../../../../providers/Ocean' -import { useWeb3 } from '../../../../providers/Web3' import { AdvancedSettingsForm } from '../../../../models/FormEditCredential' +import FormActions from './FormActions' export default function FormAdvancedSettings({ data, @@ -15,10 +13,7 @@ export default function FormAdvancedSettings({ data: FormFieldProps[] setShowEdit: (show: boolean) => void }): ReactElement { - const { accountId } = useWeb3() - const { ocean, config } = useOcean() const { - isValid, validateField, setFieldValue }: FormikContextType<Partial<AdvancedSettingsForm>> = useFormikContext() @@ -46,14 +41,7 @@ export default function FormAdvancedSettings({ /> ))} - <footer className={styles.actions}> - <Button style="primary" disabled={!ocean || !accountId || !isValid}> - Submit - </Button> - <Button style="text" onClick={() => setShowEdit(false)}> - Cancel - </Button> - </footer> + <FormActions setShowEdit={setShowEdit} /> </Form> ) } diff --git a/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx b/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx index 2605d60f1..5104fbf87 100644 --- a/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx +++ b/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx @@ -1,9 +1,6 @@ import React, { ReactElement, useEffect, useState } from 'react' import { Field, Form, FormikContextType, useFormikContext } from 'formik' -import Button from '../../../atoms/Button' import Input from '../../../atoms/Input' -import { useOcean } from '../../../../providers/Ocean' -import { useWeb3 } from '../../../../providers/Web3' import { FormFieldProps } from '../../../../@types/Form' import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection' import stylesIndex from './index.module.css' @@ -16,6 +13,8 @@ import { useAsset } from '../../../../providers/Asset' import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset' import { publisherTrustedAlgorithm as PublisherTrustedAlgorithm } from '@oceanprotocol/lib' import axios from 'axios' +import { useSiteMetadata } from '../../../../hooks/useSiteMetadata' +import FormActions from './FormActions' export default function FormEditComputeDataset({ data, @@ -26,11 +25,9 @@ export default function FormEditComputeDataset({ title: string setShowEdit: (show: boolean) => void }): ReactElement { - const { accountId } = useWeb3() - const { ocean, config } = useOcean() + const { appConfig } = useSiteMetadata() const { ddo } = useAsset() - const { isValid, values }: FormikContextType<ComputePrivacyForm> = - useFormikContext() + const { values }: FormikContextType<ComputePrivacyForm> = useFormikContext() const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>() const { publisherTrustedAlgorithms } = @@ -44,21 +41,16 @@ export default function FormEditComputeDataset({ offset: 500, query: { query_string: { - query: `service.attributes.main.type:algorithm -isInPurgatory:true` + query: `service.attributes.main.type:algorithm AND chainId:${ddo.chainId} -isInPurgatory:true` } }, sort: { created: -1 } } - const querryResult = await queryMetadata( - query, - config.metadataCacheUri, - source.token - ) + const querryResult = await queryMetadata(query, source.token) const datasetComputeService = ddo.findServiceByType('compute') const algorithmSelectionList = await transformDDOToAssetSelection( datasetComputeService?.serviceEndpoint, querryResult.results, - config.metadataCacheUri, publisherTrustedAlgorithms ) return algorithmSelectionList @@ -68,7 +60,7 @@ export default function FormEditComputeDataset({ getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => { setAllAlgorithms(algorithms) }) - }, [config.metadataCacheUri, publisherTrustedAlgorithms]) + }, [appConfig.metadataCacheUri, publisherTrustedAlgorithms]) return ( <Form className={styles.form}> @@ -90,14 +82,8 @@ export default function FormEditComputeDataset({ component={Input} /> ))} - <footer className={styles.actions}> - <Button style="primary" disabled={!ocean || !accountId || !isValid}> - Submit - </Button> - <Button style="text" onClick={() => setShowEdit(false)}> - Cancel - </Button> - </footer> + + <FormActions setShowEdit={setShowEdit} /> </Form> ) } diff --git a/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css b/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css index 4bb393ac4..b20a7a782 100644 --- a/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css +++ b/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css @@ -2,27 +2,6 @@ composes: box from '../../../atoms/Box.module.css'; } -.actions { - margin-left: -2rem; - margin-right: -2rem; - border-top: 1px solid var(--border-color); - padding: calc(var(--spacer) / 2) var(--spacer) 0; - display: flex; - justify-content: center; -} - -@media (min-width: 40rem) { - .actions { - padding-top: var(--spacer); - } -} - -.actions a, -.actions button { - margin-left: calc(var(--spacer) / 2); - margin-right: calc(var(--spacer) / 2); -} - -select[multiple] { +.form select[multiple] { height: 130px; } diff --git a/src/components/organisms/AssetActions/Edit/FormEditMetadata.tsx b/src/components/organisms/AssetActions/Edit/FormEditMetadata.tsx index 1c8d477ac..840e1d9e3 100644 --- a/src/components/organisms/AssetActions/Edit/FormEditMetadata.tsx +++ b/src/components/organisms/AssetActions/Edit/FormEditMetadata.tsx @@ -1,13 +1,12 @@ import React, { ChangeEvent, ReactElement } from 'react' -import styles from './FormEditMetadata.module.css' import { Field, Form, FormikContextType, useFormikContext } from 'formik' -import Button from '../../../atoms/Button' +import { useOcean } from '../../../../providers/Ocean' import Input from '../../../atoms/Input' import { FormFieldProps } from '../../../../@types/Form' import { MetadataPublishFormDataset } from '../../../../@types/MetaData' import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata' -import { useOcean } from '../../../../providers/Ocean' -import { useWeb3 } from '../../../../providers/Web3' +import FormActions from './FormActions' +import styles from './FormEditMetadata.module.css' function handleTimeoutCustomOption( data: FormFieldProps[], @@ -56,10 +55,8 @@ export default function FormEditMetadata({ values: Partial<MetadataPublishFormDataset> showPrice: boolean }): ReactElement { - const { accountId } = useWeb3() - const { ocean, config } = useOcean() + const { config } = useOcean() const { - isValid, validateField, setFieldValue }: FormikContextType<Partial<MetadataPublishFormDataset>> = useFormikContext() @@ -96,18 +93,10 @@ export default function FormEditMetadata({ ) )} - <footer className={styles.actions}> - <Button - style="primary" - disabled={!ocean || !accountId || !isValid} - onClick={() => setTimeoutStringValue(values.timeout)} - > - Submit - </Button> - <Button style="text" onClick={() => setShowEdit(false)}> - Cancel - </Button> - </footer> + <FormActions + setShowEdit={setShowEdit} + handleClick={() => setTimeoutStringValue(values.timeout)} + /> </Form> ) } diff --git a/src/components/organisms/AssetActions/Edit/index.tsx b/src/components/organisms/AssetActions/Edit/index.tsx index a513e4854..cea8e903b 100644 --- a/src/components/organisms/AssetActions/Edit/index.tsx +++ b/src/components/organisms/AssetActions/Edit/index.tsx @@ -9,7 +9,7 @@ import { useAsset } from '../../../../providers/Asset' import { useUserPreferences } from '../../../../providers/UserPreferences' import { MetadataPreview } from '../../../molecules/MetadataPreview' import Debug from './DebugEditMetadata' -import Web3Feedback from '../../../molecules/Wallet/Feedback' +import Web3Feedback from '../../../molecules/Web3Feedback' import FormEditMetadata from './FormEditMetadata' import { mapTimeoutStringToSeconds } from '../../../../utils/metadata' import styles from './index.module.css' diff --git a/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx b/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx index d36281249..81ca0f4d8 100644 --- a/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx +++ b/src/components/organisms/AssetActions/Pool/Add/FormAdd.tsx @@ -13,6 +13,7 @@ import { FormAddLiquidity } from '.' import { PoolBalance } from '../../../../../@types/TokenBalance' import UserLiquidity from '../../../../atoms/UserLiquidity' import { useOcean } from '../../../../../providers/Ocean' +import { useWeb3 } from '../../../../../providers/Web3' export default function FormAdd({ coin, @@ -37,7 +38,8 @@ export default function FormAdd({ setNewPoolTokens: (value: string) => void setNewPoolShare: (value: string) => void }): ReactElement { - const { ocean, balance } = useOcean() + const { balance } = useWeb3() + const { ocean } = useOcean() // Connect with form const { diff --git a/src/components/organisms/AssetActions/Pool/Add/index.tsx b/src/components/organisms/AssetActions/Pool/Add/index.tsx index 555f4266f..36af0aa29 100644 --- a/src/components/organisms/AssetActions/Pool/Add/index.tsx +++ b/src/components/organisms/AssetActions/Pool/Add/index.tsx @@ -65,8 +65,8 @@ export default function Add({ const data = useStaticQuery(contentQuery) const content = data.content.edges[0].node.childContentJson.pool.add - const { accountId } = useWeb3() - const { ocean, balance } = useOcean() + const { accountId, balance } = useWeb3() + const { ocean } = useOcean() const { debug } = useUserPreferences() const [txId, setTxId] = useState<string>() const [coin, setCoin] = useState('OCEAN') diff --git a/src/components/organisms/AssetActions/Pool/Graph.tsx b/src/components/organisms/AssetActions/Pool/Graph.tsx index a20275d26..24d1d8c7e 100644 --- a/src/components/organisms/AssetActions/Pool/Graph.tsx +++ b/src/components/organisms/AssetActions/Pool/Graph.tsx @@ -8,7 +8,6 @@ import { ChartTooltipItem, ChartTooltipOptions } from 'chart.js' -import styles from './Graph.module.css' import Loader from '../../../atoms/Loader' import { formatPrice } from '../../../atoms/Price/PriceUnit' import { useUserPreferences } from '../../../../providers/UserPreferences' @@ -17,8 +16,10 @@ import { darkModeConfig } from '../../../../../app.config' import Button from '../../../atoms/Button' import { Logger } from '@oceanprotocol/lib' import { useAsset } from '../../../../providers/Asset' -import { gql, useQuery } from '@apollo/client' +import { gql, OperationResult } from 'urql' import { PoolHistory } from '../../../../@types/apollo/PoolHistory' +import { fetchData, getQueryContext } from '../../../../utils/subgraph' +import styles from './Graph.module.css' declare type GraphType = 'liquidity' | 'price' @@ -27,6 +28,8 @@ defaults.global.defaultFontFamily = `'Sharp Sans', -apple-system, BlinkMacSystem 'Segoe UI', Helvetica, Arial, sans-serif` defaults.global.animation = { easing: 'easeInOutQuart', duration: 1000 } +const REFETCH_INTERVAL = 10000 + const lineStyle: Partial<ChartDataSets> = { fill: false, lineTension: 0.1, @@ -121,22 +124,53 @@ export default function Graph(): ReactElement { const [options, setOptions] = useState<ChartOptions>() const [graphType, setGraphType] = useState<GraphType>('liquidity') - const { price } = useAsset() + const { price, ddo } = useAsset() const [lastBlock, setLastBlock] = useState<number>(0) const [priceHistory, setPriceHistory] = useState([]) + const [error, setError] = useState<Error>() const [liquidityHistory, setLiquidityHistory] = useState([]) const [timestamps, setTimestamps] = useState([]) const [isLoading, setIsLoading] = useState(true) + const [dataHistory, setDataHistory] = useState<PoolHistory>() const [graphData, setGraphData] = useState<ChartData>() + const [graphFetchInterval, setGraphFetchInterval] = useState<NodeJS.Timeout>() - const { data, refetch, error } = useQuery<PoolHistory>(poolHistory, { - variables: { - id: price.address.toLowerCase(), - block: lastBlock - }, - pollInterval: 20000 - }) + async function getPoolHistory() { + try { + const queryContext = getQueryContext(ddo.chainId) + const queryVariables = { + id: price.address.toLowerCase(), + block: lastBlock + } + + const queryResult: OperationResult<PoolHistory> = await fetchData( + poolHistory, + queryVariables, + queryContext + ) + setDataHistory(queryResult?.data) + } catch (error) { + console.error('Error fetchData: ', error.message) + setError(error) + } + } + + function refetchGraph() { + if (!graphFetchInterval) { + setGraphFetchInterval( + setInterval(function () { + getPoolHistory() + }, REFETCH_INTERVAL) + ) + } + } + + useEffect(() => { + return () => { + clearInterval(graphFetchInterval) + } + }, [graphFetchInterval]) useEffect(() => { Logger.log('Fired GraphOptions!') @@ -145,59 +179,70 @@ export default function Graph(): ReactElement { }, [locale, darkMode.value]) useEffect(() => { - if (!data) return - Logger.log('Fired GraphData!') + getPoolHistory() + }, [lastBlock]) - const latestTimestamps = [ - ...timestamps, - ...data.poolTransactions.map((item) => { - const date = new Date(item.timestamp * 1000) - return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}` - }) - ] - setTimestamps(latestTimestamps) + useEffect(() => { + async function init() { + const data: PoolHistory = dataHistory + if (!data) { + await getPoolHistory() + return + } + Logger.log('Fired GraphData!') - const latestLiquidtyHistory = [ - ...liquidityHistory, - ...data.poolTransactions.map((item) => item.oceanReserve) - ] + const latestTimestamps = [ + ...timestamps, + ...data.poolTransactions.map((item) => { + const date = new Date(item.timestamp * 1000) + return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}` + }) + ] + setTimestamps(latestTimestamps) - setLiquidityHistory(latestLiquidtyHistory) + const latestLiquidtyHistory = [ + ...liquidityHistory, + ...data.poolTransactions.map((item) => item.oceanReserve) + ] - const latestPriceHistory = [ - ...priceHistory, - ...data.poolTransactions.map((item) => item.spotPrice) - ] + setLiquidityHistory(latestLiquidtyHistory) - setPriceHistory(latestPriceHistory) + const latestPriceHistory = [ + ...priceHistory, + ...data.poolTransactions.map((item) => item.spotPrice) + ] - if (data.poolTransactions.length > 0) { - const newBlock = - data.poolTransactions[data.poolTransactions.length - 1].block - if (newBlock === lastBlock) return - setLastBlock( - data.poolTransactions[data.poolTransactions.length - 1].block - ) - refetch() - } else { - setGraphData({ - labels: latestTimestamps.slice(0), - datasets: [ - { - ...lineStyle, - label: 'Liquidity (OCEAN)', - data: - graphType === 'liquidity' - ? latestLiquidtyHistory.slice(0) - : latestPriceHistory.slice(0), - borderColor: `#8b98a9`, - pointBackgroundColor: `#8b98a9` - } - ] - }) - setIsLoading(false) + setPriceHistory(latestPriceHistory) + + if (data.poolTransactions.length > 0) { + const newBlock = + data.poolTransactions[data.poolTransactions.length - 1].block + if (newBlock === lastBlock) return + setLastBlock( + data.poolTransactions[data.poolTransactions.length - 1].block + ) + } else { + setGraphData({ + labels: latestTimestamps.slice(0), + datasets: [ + { + ...lineStyle, + label: 'Liquidity (OCEAN)', + data: + graphType === 'liquidity' + ? latestLiquidtyHistory.slice(0) + : latestPriceHistory.slice(0), + borderColor: `#8b98a9`, + pointBackgroundColor: `#8b98a9` + } + ] + }) + setIsLoading(false) + refetchGraph() + } } - }, [data, graphType]) + init() + }, [dataHistory, graphType]) function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) { e.preventDefault() diff --git a/src/components/organisms/AssetActions/Pool/TokenList.tsx b/src/components/organisms/AssetActions/Pool/TokenList.tsx index a04fcc790..50cafa339 100644 --- a/src/components/organisms/AssetActions/Pool/TokenList.tsx +++ b/src/components/organisms/AssetActions/Pool/TokenList.tsx @@ -11,7 +11,8 @@ export default function TokenList({ dtSymbol, poolShares, conversion, - highlight + highlight, + showTVLLabel }: { title: string | ReactNode children: ReactNode @@ -21,6 +22,7 @@ export default function TokenList({ poolShares: string conversion: number highlight?: boolean + showTVLLabel?: boolean }): ReactElement { return ( <div className={`${styles.tokenlist} ${highlight ? styles.highlight : ''}`}> @@ -33,6 +35,7 @@ export default function TokenList({ <Conversion price={`${conversion}`} className={styles.totalLiquidity} + showTVLLabel={showTVLLabel} /> )} </div> diff --git a/src/components/organisms/AssetActions/Pool/index.tsx b/src/components/organisms/AssetActions/Pool/index.tsx index ea13e9d87..cb26f3af9 100644 --- a/src/components/organisms/AssetActions/Pool/index.tsx +++ b/src/components/organisms/AssetActions/Pool/index.tsx @@ -15,10 +15,13 @@ import { PoolBalance } from '../../../../@types/TokenBalance' import Transactions from './Transactions' import Graph from './Graph' import { useAsset } from '../../../../providers/Asset' -import { gql, useQuery } from '@apollo/client' +import { gql, OperationResult } from 'urql' import { PoolLiquidity } from '../../../../@types/apollo/PoolLiquidity' import { useOcean } from '../../../../providers/Ocean' import { useWeb3 } from '../../../../providers/Web3' +import { fetchData, getQueryContext } from '../../../../utils/subgraph' + +const REFETCH_INTERVAL = 5000 const contentQuery = graphql` query PoolQuery { @@ -62,9 +65,10 @@ export default function Pool(): ReactElement { const data = useStaticQuery(contentQuery) const content = data.content.edges[0].node.childContentJson.pool - const { accountId, networkId } = useWeb3() + const { accountId } = useWeb3() const { ocean } = useOcean() - const { isInPurgatory, ddo, owner, price, refreshInterval } = useAsset() + const { isInPurgatory, ddo, owner, price, refreshInterval, isAssetNetwork } = + useAsset() const dtSymbol = ddo?.dataTokenInfo.symbol const [poolTokens, setPoolTokens] = useState<string>() @@ -88,20 +92,50 @@ export default function Pool(): ReactElement { const [creatorLiquidity, setCreatorLiquidity] = useState<PoolBalance>() const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>() const [creatorPoolShare, setCreatorPoolShare] = useState<string>() + const [dataLiquidity, setdataLiquidity] = useState<PoolLiquidity>() + const [liquidityFetchInterval, setLiquidityFetchInterval] = + useState<NodeJS.Timeout>() // the purpose of the value is just to trigger the effect const [refreshPool, setRefreshPool] = useState(false) - const { data: dataLiquidity } = useQuery<PoolLiquidity>(poolLiquidityQuery, { - variables: { + + async function getPoolLiquidity() { + const queryContext = getQueryContext(ddo.chainId) + const queryVariables = { id: price.address.toLowerCase(), shareId: `${price.address.toLowerCase()}-${ddo.publicKey[0].owner.toLowerCase()}` - }, - pollInterval: 5000 - }) + } + + const queryResult: OperationResult<PoolLiquidity> = await fetchData( + poolLiquidityQuery, + queryVariables, + queryContext + ) + setdataLiquidity(queryResult?.data) + } + + function refetchLiquidity() { + if (!liquidityFetchInterval) { + setLiquidityFetchInterval( + setInterval(function () { + getPoolLiquidity() + }, REFETCH_INTERVAL) + ) + } + } + + useEffect(() => { + return () => { + clearInterval(liquidityFetchInterval) + } + }, [liquidityFetchInterval]) useEffect(() => { async function init() { - if (!dataLiquidity || !dataLiquidity.pool) return + if (!dataLiquidity || !dataLiquidity.pool) { + await getPoolLiquidity() + return + } // Total pool shares const totalPoolTokens = dataLiquidity.pool.totalShares @@ -149,6 +183,7 @@ export default function Pool(): ReactElement { creatorLiquidity && ((Number(creatorPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2) setCreatorPoolShare(creatorPoolShare) + refetchLiquidity() } init() }, [dataLiquidity, ddo.dataToken, price.datatoken, price.ocean, price?.value]) @@ -243,15 +278,15 @@ export default function Pool(): ReactElement { <Tooltip content={content.tooltips.price} /> <div className={styles.dataTokenLinks}> <ExplorerLink - networkId={networkId} + networkId={ddo.chainId} path={`address/${price?.address}`} > Pool </ExplorerLink> <ExplorerLink - networkId={networkId} + networkId={ddo.chainId} path={ - networkId === 137 || networkId === 1287 + ddo.chainId === 2021000 || ddo.chainId === 1287 ? `tokens/${ddo.dataToken}` : `token/${ddo.dataToken}` } @@ -284,7 +319,7 @@ export default function Pool(): ReactElement { </TokenList> <TokenList - title="Pool Creator Liquidity" + title="Pool Creator Statistics" ocean={`${creatorLiquidity?.ocean}`} dt={`${creatorLiquidity?.datatoken}`} dtSymbol={dtSymbol} @@ -314,6 +349,7 @@ export default function Pool(): ReactElement { dtSymbol={dtSymbol} poolShares={totalPoolTokens} conversion={totalLiquidityInOcean} + showTVLLabel > <Token symbol="% swap fee" balance={swapFee} noIcon /> </TokenList> @@ -330,14 +366,18 @@ export default function Pool(): ReactElement { style="primary" size="small" onClick={() => setShowAdd(true)} - disabled={isInPurgatory} + disabled={isInPurgatory || !isAssetNetwork} > Add Liquidity </Button> )} {hasAddedLiquidity && !isRemoveDisabled && ( - <Button size="small" onClick={() => setShowRemove(true)}> + <Button + size="small" + onClick={() => setShowRemove(true)} + disabled={!isAssetNetwork} + > Remove </Button> )} diff --git a/src/components/organisms/AssetActions/Trade/FormTrade.tsx b/src/components/organisms/AssetActions/Trade/FormTrade.tsx index f04bd5704..1188d5618 100644 --- a/src/components/organisms/AssetActions/Trade/FormTrade.tsx +++ b/src/components/organisms/AssetActions/Trade/FormTrade.tsx @@ -14,6 +14,7 @@ import { FormTradeData, initialValues } from '../../../../models/FormTrade' import Decimal from 'decimal.js' import { useOcean } from '../../../../providers/Ocean' import { useWeb3 } from '../../../../providers/Web3' +import { useAsset } from '../../../../providers/Asset' const contentQuery = graphql` query TradeQuery { @@ -49,6 +50,7 @@ export default function FormTrade({ const content = data.content.edges[0].node.childContentJson.trade const { accountId } = useWeb3() const { ocean } = useOcean() + const { isAssetNetwork } = useAsset() const { debug } = useUserPreferences() const [txId, setTxId] = useState<string>() @@ -139,7 +141,7 @@ export default function FormTrade({ </div> )} <Actions - isDisabled={!isWarningAccepted} + isDisabled={!isWarningAccepted || !isAssetNetwork} isLoading={isSubmitting} loaderMessage="Swapping tokens..." successMessage="Successfully swapped tokens." diff --git a/src/components/organisms/AssetActions/Trade/index.tsx b/src/components/organisms/AssetActions/Trade/index.tsx index 6079a5d55..14b45f1cf 100644 --- a/src/components/organisms/AssetActions/Trade/index.tsx +++ b/src/components/organisms/AssetActions/Trade/index.tsx @@ -6,8 +6,8 @@ import { useOcean } from '../../../../providers/Ocean' import { useWeb3 } from '../../../../providers/Web3' export default function Trade(): ReactElement { - const { accountId } = useWeb3() - const { ocean, balance } = useOcean() + const { accountId, balance } = useWeb3() + const { ocean } = useOcean() const [tokenBalance, setTokenBalance] = useState<PoolBalance>() const { price, ddo } = useAsset() const [maxDt, setMaxDt] = useState(0) diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx index 8028f494d..1a88cb9dd 100644 --- a/src/components/organisms/AssetActions/index.tsx +++ b/src/components/organisms/AssetActions/index.tsx @@ -11,13 +11,14 @@ import Trade from './Trade' import { useAsset } from '../../../providers/Asset' import { useOcean } from '../../../providers/Ocean' import { useWeb3 } from '../../../providers/Web3' +import Web3Feedback from '../../molecules/Web3Feedback' import { getFileInfo } from '../../../utils/provider' import axios from 'axios' export default function AssetActions(): ReactElement { - const { accountId } = useWeb3() - const { config, ocean, balance, account } = useOcean() - const { price, ddo } = useAsset() + const { accountId, balance } = useWeb3() + const { ocean, config, account } = useOcean() + const { price, ddo, isAssetNetwork } = useAsset() const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>() const [dtBalance, setDtBalance] = useState<string>() @@ -31,7 +32,7 @@ export default function AssetActions(): ReactElement { useEffect(() => { if (!ddo || !accountId) return async function checkIsConsumable() { - const consumable = await ocean.assets.isConsumable( + const consumable: any = await ocean.assets.isConsumable( ddo, accountId.toLowerCase() ) @@ -72,6 +73,7 @@ export default function AssetActions(): ReactElement { // Get and set user DT balance useEffect(() => { if (!ocean || !accountId) return + async function init() { try { const dtBalance = await ocean.datatokens.balance( @@ -84,7 +86,7 @@ export default function AssetActions(): ReactElement { } } init() - }, [ocean, accountId, ddo.dataToken]) + }, [ocean, accountId, ddo.dataToken, isAssetNetwork]) // Check user balance against price useEffect(() => { @@ -141,8 +143,14 @@ export default function AssetActions(): ReactElement { ) return ( - <Permission eventType="consume"> - <Tabs items={tabs} className={styles.actions} /> - </Permission> + <> + <Permission eventType="consume"> + <Tabs items={tabs} className={styles.actions} /> + </Permission> + <Web3Feedback + isBalanceSufficient={isBalanceSufficient} + isAssetNetwork={isAssetNetwork} + /> + </> ) } diff --git a/src/components/organisms/AssetContent/AlgorithmDatasetsListForCompute.tsx b/src/components/organisms/AssetContent/AlgorithmDatasetsListForCompute.tsx index ab5c2516a..e96b5c7d2 100644 --- a/src/components/organisms/AssetContent/AlgorithmDatasetsListForCompute.tsx +++ b/src/components/organisms/AssetContent/AlgorithmDatasetsListForCompute.tsx @@ -3,7 +3,6 @@ import styles from './AlgorithmDatasetsListForCompute.module.css' import { getAlgorithmDatasetsForCompute } from '../../../utils/aquarius' import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection' import AssetComputeList from '../../molecules/AssetComputeList' -import { useOcean } from '../../../providers/Ocean' import { useAsset } from '../../../providers/Asset' import { DDO } from '@oceanprotocol/lib' @@ -14,7 +13,6 @@ export default function AlgorithmDatasetsListForCompute({ algorithmDid: string dataset: DDO }): ReactElement { - const { config } = useOcean() const { type } = useAsset() const [datasetsForCompute, setDatasetsForCompute] = useState<AssetSelectionAsset[]>() @@ -28,7 +26,7 @@ export default function AlgorithmDatasetsListForCompute({ const datasets = await getAlgorithmDatasetsForCompute( algorithmDid, datasetComputeService?.serviceEndpoint, - config.metadataCacheUri + dataset?.chainId ) setDatasetsForCompute(datasets) } diff --git a/src/components/organisms/AssetContent/Bookmark.module.css b/src/components/organisms/AssetContent/Bookmark.module.css index 01a14c1e3..ea1223abb 100644 --- a/src/components/organisms/AssetContent/Bookmark.module.css +++ b/src/components/organisms/AssetContent/Bookmark.module.css @@ -1,6 +1,6 @@ .bookmark { position: absolute; - top: -10px; + top: -3px; right: calc(var(--spacer) / 8); appearance: none; background: none; @@ -20,7 +20,7 @@ .bookmark:hover, .bookmark:focus { - transform: translate3d(0, 6px, 0); + transform: translate3d(0, -3px, 0); } .bookmark.active svg { diff --git a/src/components/organisms/AssetContent/Bookmark.tsx b/src/components/organisms/AssetContent/Bookmark.tsx index 06b98478b..ab777a7c0 100644 --- a/src/components/organisms/AssetContent/Bookmark.tsx +++ b/src/components/organisms/AssetContent/Bookmark.tsx @@ -2,15 +2,12 @@ import { useUserPreferences } from '../../../providers/UserPreferences' import React, { ReactElement } from 'react' import styles from './Bookmark.module.css' import { ReactComponent as BookmarkIcon } from '../../../images/bookmark.svg' -import { ConfigHelperConfig } from '@oceanprotocol/lib' import { useOcean } from '../../../providers/Ocean' export default function Bookmark({ did }: { did: string }): ReactElement { const { config } = useOcean() const { bookmarks, addBookmark, removeBookmark } = useUserPreferences() - const isBookmarked = - bookmarks && - bookmarks[(config as ConfigHelperConfig).network]?.includes(did) + const isBookmarked = bookmarks && bookmarks?.includes(did) function handleBookmark() { isBookmarked ? removeBookmark(did) : addBookmark(did) diff --git a/src/components/organisms/AssetContent/EditHistory.tsx b/src/components/organisms/AssetContent/EditHistory.tsx index 6ef9b453c..38cf34e45 100644 --- a/src/components/organisms/AssetContent/EditHistory.tsx +++ b/src/components/organisms/AssetContent/EditHistory.tsx @@ -3,7 +3,7 @@ import { useAsset } from '../../../providers/Asset' import ExplorerLink from '../../atoms/ExplorerLink' import Time from '../../atoms/Time' import styles from './EditHistory.module.css' -import { gql, useQuery } from '@apollo/client' +import { gql, useQuery } from 'urql' import { ReceiptData_datatokens_updates as ReceiptData } from '../../../@types/apollo/ReceiptData' import { useWeb3 } from '../../../providers/Web3' @@ -22,9 +22,11 @@ const getReceipts = gql` export default function EditHistory(): ReactElement { const { networkId } = useWeb3() const { ddo } = useAsset() - const { data } = useQuery(getReceipts, { + const [result] = useQuery({ + query: getReceipts, variables: { address: ddo?.dataToken.toLowerCase() } }) + const { data } = result const [receipts, setReceipts] = useState<ReceiptData[]>() const [creationTx, setCreationTx] = useState<string>() @@ -48,14 +50,14 @@ export default function EditHistory(): ReactElement { <ul className={styles.history}> {receipts?.map((receipt) => ( <li key={receipt.id} className={styles.item}> - <ExplorerLink networkId={networkId} path={`/tx/${receipt.tx}`}> + <ExplorerLink networkId={ddo.chainId} path={`/tx/${receipt.tx}`}> edited{' '} <Time date={receipt.timestamp.toString()} relative isUnix /> </ExplorerLink> </li> ))} <li className={styles.item}> - <ExplorerLink networkId={networkId} path={`/tx/${creationTx}`}> + <ExplorerLink networkId={ddo.chainId} path={`/tx/${creationTx}`}> published <Time date={ddo.created} relative /> </ExplorerLink> </li> diff --git a/src/components/organisms/AssetContent/MetaMain.tsx b/src/components/organisms/AssetContent/MetaMain.tsx index f1ea6ca80..8dfc8d375 100644 --- a/src/components/organisms/AssetContent/MetaMain.tsx +++ b/src/components/organisms/AssetContent/MetaMain.tsx @@ -9,7 +9,7 @@ import AssetType from '../../atoms/AssetType' import styles from './MetaMain.module.css' export default function MetaMain(): ReactElement { - const { ddo, owner, type } = useAsset() + const { ddo, owner, type, isAssetNetwork } = useAsset() const { networkId, web3ProviderInfo } = useWeb3() const isCompute = Boolean(ddo?.findServiceByType('compute')) const accessType = isCompute ? 'compute' : 'access' @@ -34,7 +34,7 @@ export default function MetaMain(): ReactElement { {`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`} </ExplorerLink> - {web3ProviderInfo?.name === 'MetaMask' && ( + {web3ProviderInfo?.name === 'MetaMask' && isAssetNetwork && ( <span className={styles.addWrap}> <AddToken address={ddo?.dataTokenInfo.address} diff --git a/src/components/organisms/AssetContent/Pricing/FormPricing/Dynamic.tsx b/src/components/organisms/AssetContent/Pricing/FormPricing/Dynamic.tsx index 76d751b50..3c53aca63 100644 --- a/src/components/organisms/AssetContent/Pricing/FormPricing/Dynamic.tsx +++ b/src/components/organisms/AssetContent/Pricing/FormPricing/Dynamic.tsx @@ -23,8 +23,8 @@ export default function Dynamic({ ddo: DDO content: any }): ReactElement { - const { networkId } = useWeb3() - const { account, balance } = useOcean() + const { networkId, balance } = useWeb3() + const { account } = useOcean() const [firstPrice, setFirstPrice] = useState<string>() // Connect with form diff --git a/src/components/organisms/AssetContent/index.module.css b/src/components/organisms/AssetContent/index.module.css index b54048e13..df8a9c43d 100644 --- a/src/components/organisms/AssetContent/index.module.css +++ b/src/components/organisms/AssetContent/index.module.css @@ -1,8 +1,13 @@ +.networkWrap { + display: block; + margin-top: -1rem; +} + .grid { display: grid; gap: calc(var(--spacer) * 1.5); position: relative; - margin-top: -1.5rem; + margin-top: -1rem; } .grid > div { diff --git a/src/components/organisms/AssetContent/index.tsx b/src/components/organisms/AssetContent/index.tsx index 3f695b864..56edfbb68 100644 --- a/src/components/organisms/AssetContent/index.tsx +++ b/src/components/organisms/AssetContent/index.tsx @@ -19,6 +19,7 @@ import { useWeb3 } from '../../../providers/Web3' import styles from './index.module.css' import EditAdvancedSettings from '../AssetActions/Edit/EditAdvancedSettings' import { useSiteMetadata } from '../../../hooks/useSiteMetadata' +import NetworkName from '../../atoms/NetworkName' export interface AssetContentProps { path?: string @@ -46,7 +47,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement { const content = data.purgatory.edges[0].node.childContentJson.asset const { debug } = useUserPreferences() const { accountId } = useWeb3() - const { owner, isInPurgatory, purgatoryData } = useAsset() + const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset() const [showPricing, setShowPricing] = useState(false) const [showEdit, setShowEdit] = useState<boolean>() const [showEditCompute, setShowEditCompute] = useState<boolean>() @@ -87,72 +88,82 @@ export default function AssetContent(props: AssetContentProps): ReactElement { ) : showEditAdvancedSettings ? ( <EditAdvancedSettings setShowEdit={setShowEditAdvancedSettings} /> ) : ( - <article className={styles.grid}> - <div> - {showPricing && <Pricing ddo={ddo} />} - <div className={styles.content}> - <MetaMain /> - <Bookmark did={ddo.id} /> + <> + <div className={styles.networkWrap}> + <NetworkName networkId={ddo.chainId} className={styles.network} /> + </div> - {isInPurgatory ? ( - <Alert - title={content.title} - badge={`Reason: ${purgatoryData?.reason}`} - text={content.description} - state="error" - /> - ) : ( - <> - <Markdown - className={styles.description} - text={metadata?.additionalInformation?.description || ''} + <article className={styles.grid}> + <div> + {showPricing && <Pricing ddo={ddo} />} + <div className={styles.content}> + <MetaMain /> + <Bookmark did={ddo.id} /> + + {isInPurgatory ? ( + <Alert + title={content.title} + badge={`Reason: ${purgatoryData?.reason}`} + text={content.description} + state="error" /> + ) : ( + <> + <Markdown + className={styles.description} + text={metadata?.additionalInformation?.description || ''} + /> - <MetaSecondary /> + <MetaSecondary /> - {isOwner && ( - <div className={styles.ownerActions}> - <Button style="text" size="small" onClick={handleEditButton}> - Edit Metadata - </Button> - {appConfig.allowAdvancedSettings === 'true' && ( - <> - <span className={styles.separator}>|</span> - <Button - style="text" - size="small" - onClick={handleEditAdvancedSettingsButton} - > - Edit Advanced Settings - </Button> - </> - )} - {ddo.findServiceByType('compute') && type === 'dataset' && ( - <> - <span className={styles.separator}>|</span> - <Button - style="text" - size="small" - onClick={handleEditComputeButton} - > - Edit Compute Settings - </Button> - </> - )} - </div> - )} - </> - )} + {isOwner && isAssetNetwork && ( + <div className={styles.ownerActions}> + <Button + style="text" + size="small" + onClick={handleEditButton} + > + Edit Metadata + </Button> + {appConfig.allowAdvancedSettings === 'true' && ( + <> + <span className={styles.separator}>|</span> + <Button + style="text" + size="small" + onClick={handleEditAdvancedSettingsButton} + > + Edit Advanced Settings + </Button> + </> + )} + {ddo.findServiceByType('compute') && type === 'dataset' && ( + <> + <span className={styles.separator}>|</span> + <Button + style="text" + size="small" + onClick={handleEditComputeButton} + > + Edit Compute Settings + </Button> + </> + )} + </div> + )} + </> + )} - <MetaFull /> - <EditHistory /> - {debug === true && <DebugOutput title="DDO" output={ddo} />} + <MetaFull /> + <EditHistory /> + {debug === true && <DebugOutput title="DDO" output={ddo} />} + </div> </div> - </div> - <div className={styles.actions}> - <AssetActions /> - </div> - </article> + <div className={styles.actions}> + <AssetActions /> + </div> + </article> + </> ) } diff --git a/src/components/organisms/Footer.tsx b/src/components/organisms/Footer.tsx index 075ac2dda..14d74cd7b 100644 --- a/src/components/organisms/Footer.tsx +++ b/src/components/organisms/Footer.tsx @@ -14,7 +14,8 @@ export default function Footer(): ReactElement { return ( <footer className={styles.footer}> <div className={styles.content}> - <SyncStatus /> | <BuildId /> + {/* <SyncStatus /> | */} + <BuildId /> <MarketStats /> <div className={styles.copyright}> © {year} <Markdown text={copyright} /> —{' '} diff --git a/src/components/pages/History/ComputeJobs/Details.tsx b/src/components/pages/History/ComputeJobs/Details.tsx index c1bede935..1cc5fef97 100644 --- a/src/components/pages/History/ComputeJobs/Details.tsx +++ b/src/components/pages/History/ComputeJobs/Details.tsx @@ -10,6 +10,7 @@ import { retrieveDDO } from '../../../../utils/aquarius' import { useOcean } from '../../../../providers/Ocean' import Results from './Results' import styles from './Details.module.css' +import { useSiteMetadata } from '../../../../hooks/useSiteMetadata' function Asset({ title, @@ -41,7 +42,7 @@ function Asset({ } function DetailsAssets({ job }: { job: ComputeJobMetaData }) { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const [algoName, setAlgoName] = useState<string>() const [algoDtSymbol, setAlgoDtSymbol] = useState<string>() @@ -49,18 +50,14 @@ function DetailsAssets({ job }: { job: ComputeJobMetaData }) { async function getAlgoMetadata() { const source = axios.CancelToken.source() - const ddo = await retrieveDDO( - job.algoDID, - config.metadataCacheUri, - source.token - ) + const ddo = await retrieveDDO(job.algoDID, source.token) setAlgoDtSymbol(ddo.dataTokenInfo.symbol) const { attributes } = ddo.findServiceByType('metadata') setAlgoName(attributes?.main.name) } getAlgoMetadata() - }, [config?.metadataCacheUri, job.algoDID]) + }, [appConfig.metadataCacheUri, job.algoDID]) return ( <> diff --git a/src/components/pages/History/ComputeJobs/index.tsx b/src/components/pages/History/ComputeJobs/index.tsx index 688bf3f28..8d3ea9bed 100644 --- a/src/components/pages/History/ComputeJobs/index.tsx +++ b/src/components/pages/History/ComputeJobs/index.tsx @@ -8,15 +8,25 @@ import Dotdotdot from 'react-dotdotdot' import Table from '../../../atoms/Table' import Button from '../../../atoms/Button' import { useOcean } from '../../../../providers/Ocean' -import { gql, useQuery } from '@apollo/client' +import { gql } from 'urql' import { useWeb3 } from '../../../../providers/Web3' -import { queryMetadata } from '../../../../utils/aquarius' +import { + queryMetadata, + transformChainIdsListToQuery +} from '../../../../utils/aquarius' import axios, { CancelToken } from 'axios' -import { ComputeOrders } from '../../../../@types/apollo/ComputeOrders' import Details from './Details' import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute' import { ReactComponent as Refresh } from '../../../../images/refresh.svg' import styles from './index.module.css' +import { useUserPreferences } from '../../../../providers/UserPreferences' +import { getOceanConfig } from '../../../../utils/ocean' +import { fetchDataForMultipleChains } from '../../../../utils/subgraph' +import { + OrdersData_tokenOrders as OrdersData, + OrdersData_tokenOrders_datatokenId as OrdersDatatoken +} from '../../../../@types/apollo/OrdersData' +import NetworkName from '../../../atoms/NetworkName' const getComputeOrders = gql` query ComputeOrders($user: String!) { @@ -36,6 +46,14 @@ const getComputeOrders = gql` } ` +interface TokenOrder { + id: string + serviceId: number + datatokenId: OrdersDatatoken + tx: any | null + timestamp: number +} + export function Status({ children }: { children: string }): ReactElement { return <div className={styles.status}>{children}</div> } @@ -51,6 +69,12 @@ const columns = [ ) } }, + { + name: 'Network', + selector: function getNetwork(row: ComputeJobMetaData) { + return <NetworkName networkId={row.networkId} /> + } + }, { name: 'Created', selector: function getTimeRow(row: ComputeJobMetaData) { @@ -83,73 +107,94 @@ const columns = [ async function getAssetMetadata( queryDtList: string, - metadataCacheUri: string, - cancelToken: CancelToken + cancelToken: CancelToken, + chainIds: number[] ): Promise<DDO[]> { const queryDid = { page: 1, offset: 100, query: { query_string: { - query: `(${queryDtList}) AND service.attributes.main.type:dataset AND service.type:compute`, + query: `(${queryDtList}) AND (${transformChainIdsListToQuery( + chainIds + )}) AND service.attributes.main.type:dataset AND service.type:compute`, fields: ['dataToken'] } } } - const result = await queryMetadata(queryDid, metadataCacheUri, cancelToken) - + const result = await queryMetadata(queryDid, cancelToken) return result.results } export default function ComputeJobs(): ReactElement { - const { ocean, account, config } = useOcean() - const { accountId } = useWeb3() + const { ocean, account, config, connect } = useOcean() + const { accountId, networkId } = useWeb3() + const { chainIds } = useUserPreferences() const [isLoading, setIsLoading] = useState(true) const [jobs, setJobs] = useState<ComputeJobMetaData[]>([]) - const { data, refetch } = useQuery<ComputeOrders>(getComputeOrders, { - variables: { - user: accountId?.toLowerCase() + + useEffect(() => { + async function initOcean() { + const oceanInitialConfig = getOceanConfig(networkId) + await connect(oceanInitialConfig) } - }) + if (ocean === undefined) { + initOcean() + } + }, [networkId, ocean]) async function getJobs() { - if (!ocean || !account) return - + if (!accountId) return setIsLoading(true) - - await refetch() + const variables = { user: accountId?.toLowerCase() } + const result = await fetchDataForMultipleChains( + getComputeOrders, + variables, + chainIds + ) + let data: TokenOrder[] = [] + for (let i = 0; i < result.length; i++) { + if (!result[i].tokenOrders) continue + result[i].tokenOrders.forEach((tokenOrder: TokenOrder) => { + data.push(tokenOrder) + }) + } + if (!ocean || !account || !data) { + return + } + data = data.sort((a, b) => b.timestamp - a.timestamp) const dtList = [] const computeJobs: ComputeJobMetaData[] = [] - for (let i = 0; i < data.tokenOrders.length; i++) { - dtList.push(data.tokenOrders[i].datatokenId.address) + for (let i = 0; i < data.length; i++) { + dtList.push(data[i].datatokenId.address) } const queryDtList = JSON.stringify(dtList) .replace(/,/g, ' ') .replace(/"/g, '') .replace(/(\[|\])/g, '') + if (queryDtList === '') { + setJobs([]) + setIsLoading(false) + return + } try { + setIsLoading(true) const source = axios.CancelToken.source() - const assets = await getAssetMetadata( - queryDtList, - config.metadataCacheUri, - source.token - ) + const assets = await getAssetMetadata(queryDtList, source.token, chainIds) const providers: Provider[] = [] const serviceEndpoints: string[] = [] - for (let i = 0; i < data.tokenOrders.length; i++) { + for (let i = 0; i < data.length; i++) { try { const did = web3.utils - .toChecksumAddress(data.tokenOrders[i].datatokenId.address) + .toChecksumAddress(data[i].datatokenId.address) .replace('0x', 'did:op:') - const ddo = assets.filter((x) => x.id === did)[0] - - if (!ddo) continue + if (ddo === undefined) continue const service = ddo.service.filter( - (x: Service) => x.index === data.tokenOrders[i].serviceId + (x: Service) => x.index === data[i].serviceId )[0] if (!service || service.type !== 'compute') continue @@ -166,6 +211,7 @@ export default function ComputeJobs(): ReactElement { } try { + setIsLoading(true) for (let i = 0; i < serviceEndpoints.length; i++) { const instanceConfig = { config, @@ -219,7 +265,8 @@ export default function ComputeJobs(): ReactElement { const compJob: ComputeJobMetaData = { ...job, assetName: serviceMetadata.attributes.main.name, - assetDtSymbol: ddo.dataTokenInfo.symbol + assetDtSymbol: ddo.dataTokenInfo.symbol, + networkId: ddo.chainId } computeJobs.push(compJob) } @@ -237,28 +284,27 @@ export default function ComputeJobs(): ReactElement { } useEffect(() => { - if (data === undefined || !config?.metadataCacheUri) { + if (!chainIds || !accountId) { setIsLoading(false) return } getJobs() - }, [ocean, account, data, config?.metadataCacheUri]) + }, [ocean, account, chainIds, accountId]) - return ( + return accountId ? ( <> - {jobs.length > 0 && ( - <Button - style="text" - size="small" - title="Refresh compute jobs" - onClick={() => getJobs()} - disabled={isLoading} - className={styles.refresh} - > - <Refresh /> - Refresh - </Button> - )} + <Button + style="text" + size="small" + title="Refresh compute jobs" + onClick={() => getJobs()} + disabled={isLoading} + className={styles.refresh} + > + <Refresh /> + Refresh + </Button> + <Table columns={columns} data={jobs} @@ -267,5 +313,7 @@ export default function ComputeJobs(): ReactElement { defaultSortAsc={false} /> </> + ) : ( + <div>Please connect your Web3 wallet.</div> ) } diff --git a/src/components/pages/History/Downloads.tsx b/src/components/pages/History/Downloads.tsx index e96070551..f12e08f69 100644 --- a/src/components/pages/History/Downloads.tsx +++ b/src/components/pages/History/Downloads.tsx @@ -1,14 +1,18 @@ import React, { ReactElement, useEffect, useState } from 'react' import Table from '../../atoms/Table' -import { gql, useQuery } from '@apollo/client' +import { gql } from 'urql' import Time from '../../atoms/Time' import web3 from 'web3' import AssetTitle from '../../molecules/AssetListTitle' import { useWeb3 } from '../../../providers/Web3' import axios from 'axios' -import { useOcean } from '../../../providers/Ocean' import { retrieveDDO } from '../../../utils/aquarius' import { Logger } from '@oceanprotocol/lib' +import { useSiteMetadata } from '../../../hooks/useSiteMetadata' +import { useUserPreferences } from '../../../providers/UserPreferences' +import { fetchDataForMultipleChains } from '../../../utils/subgraph' +import { OrdersData_tokenOrders as OrdersData } from '../../../@types/apollo/OrdersData' +import NetworkName from '../../atoms/NetworkName' const getTokenOrders = gql` query OrdersData($user: String!) { @@ -30,7 +34,8 @@ const getTokenOrders = gql` interface DownloadedAssets { did: string dtSymbol: string - timestamp: string + timestamp: number + networkId: number } const columns = [ @@ -40,6 +45,12 @@ const columns = [ return <AssetTitle did={row.did} /> } }, + { + name: 'Network', + selector: function getNetwork(row: DownloadedAssets) { + return <NetworkName networkId={row.networkId} /> + } + }, { name: 'Datatoken', selector: function getTitleRow(row: DownloadedAssets) { @@ -56,40 +67,51 @@ const columns = [ export default function ComputeDownloads(): ReactElement { const { accountId } = useWeb3() + const { appConfig } = useSiteMetadata() const [isLoading, setIsLoading] = useState(false) const [orders, setOrders] = useState<DownloadedAssets[]>() - const { data } = useQuery(getTokenOrders, { - variables: { user: accountId?.toLowerCase() } - }) - const { config } = useOcean() + const { chainIds } = useUserPreferences() useEffect(() => { - if (!config.metadataCacheUri || !data) return + const variables = { user: accountId?.toLowerCase() } async function filterAssets() { const filteredOrders: DownloadedAssets[] = [] const source = axios.CancelToken.source() - - setIsLoading(true) try { - for (let i = 0; i < data.tokenOrders.length; i++) { + setIsLoading(true) + const response = await fetchDataForMultipleChains( + getTokenOrders, + variables, + chainIds + ) + + const data: OrdersData[] = [] + for (let i = 0; i < response.length; i++) { + response[i].tokenOrders.forEach((tokenOrder: OrdersData) => { + data.push(tokenOrder) + }) + } + + for (let i = 0; i < data.length; i++) { const did = web3.utils - .toChecksumAddress(data.tokenOrders[i].datatokenId.address) + .toChecksumAddress(data[i].datatokenId.address) .replace('0x', 'did:op:') - const ddo = await retrieveDDO( - did, - config?.metadataCacheUri, - source.token - ) + const ddo = await retrieveDDO(did, source.token) + if (!ddo) continue if (ddo.service[1].type === 'access') { filteredOrders.push({ did: did, - dtSymbol: data.tokenOrders[i].datatokenId.symbol, - timestamp: data.tokenOrders[i].timestamp + networkId: ddo.chainId, + dtSymbol: data[i].datatokenId.symbol, + timestamp: data[i].timestamp }) } } - setOrders(filteredOrders) + const sortedOrders = filteredOrders.sort( + (a, b) => b.timestamp - a.timestamp + ) + setOrders(sortedOrders) } catch (err) { Logger.log(err.message) } finally { @@ -98,15 +120,16 @@ export default function ComputeDownloads(): ReactElement { } filterAssets() - }, [config?.metadataCacheUri, data]) + }, [accountId, appConfig.metadataCacheUri, chainIds]) - return ( + return accountId ? ( <Table columns={columns} data={orders} paginationPerPage={10} isLoading={isLoading} - emptyMessage="Your downloads will show up here" /> + ) : ( + <div>Please connect your Web3 wallet.</div> ) } diff --git a/src/components/pages/History/PoolShares.tsx b/src/components/pages/History/PoolShares.tsx index f34f2ac27..2413a4318 100644 --- a/src/components/pages/History/PoolShares.tsx +++ b/src/components/pages/History/PoolShares.tsx @@ -3,7 +3,7 @@ import Table from '../../atoms/Table' import Conversion from '../../atoms/Price/Conversion' import styles from './PoolShares.module.css' import AssetTitle from '../../molecules/AssetListTitle' -import { gql, useQuery } from '@apollo/client' +import { gql } from 'urql' import { PoolShares as PoolSharesList, PoolShares_poolShares as PoolShare, @@ -12,6 +12,13 @@ import { import web3 from 'web3' import Token from '../../organisms/AssetActions/Pool/Token' import { useWeb3 } from '../../../providers/Web3' +import { useUserPreferences } from '../../../providers/UserPreferences' +import { fetchDataForMultipleChains } from '../../../utils/subgraph' +import NetworkName from '../../atoms/NetworkName' +import axios from 'axios' +import { retrieveDDO } from '../../../utils/aquarius' + +const REFETCH_INTERVAL = 20000 const poolSharesQuery = gql` query PoolShares($user: String) { @@ -35,6 +42,7 @@ const poolSharesQuery = gql` totalShares consumePrice spotPrice + createTime } } } @@ -43,6 +51,8 @@ const poolSharesQuery = gql` interface Asset { userLiquidity: number poolShare: PoolShare + networkId: number + createTime: number } function calculateUserLiquidity(poolShare: PoolShare) { @@ -116,6 +126,12 @@ const columns = [ }, grow: 2 }, + { + name: 'Network', + selector: function getNetwork(row: Asset) { + return <NetworkName networkId={row.networkId} /> + } + }, { name: 'Datatoken', selector: function getSymbol(row: Asset) { @@ -141,27 +157,85 @@ const columns = [ export default function PoolShares(): ReactElement { const { accountId } = useWeb3() const [assets, setAssets] = useState<Asset[]>() - const { data, loading } = useQuery<PoolSharesList>(poolSharesQuery, { - variables: { - user: accountId?.toLowerCase() - }, - pollInterval: 20000 - }) + const [loading, setLoading] = useState<boolean>(false) + const [data, setData] = useState<PoolShare[]>() + const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>() + const { chainIds } = useUserPreferences() + + async function fetchPoolSharesData() { + const variables = { user: accountId?.toLowerCase() } + const shares: PoolShare[] = [] + const result = await fetchDataForMultipleChains( + poolSharesQuery, + variables, + chainIds + ) + for (let i = 0; i < result.length; i++) { + result[i].poolShares.forEach((poolShare: PoolShare) => { + shares.push(poolShare) + }) + } + if (JSON.stringify(data) !== JSON.stringify(shares)) { + setData(shares) + } + } + + function refetchPoolShares() { + if (!dataFetchInterval) { + setDataFetchInterval( + setInterval(function () { + fetchPoolSharesData() + }, REFETCH_INTERVAL) + ) + } + } useEffect(() => { - if (!data) return - const assetList: Asset[] = [] - data.poolShares.forEach((poolShare) => { - const userLiquidity = calculateUserLiquidity(poolShare) - assetList.push({ - poolShare: poolShare, - userLiquidity: userLiquidity - }) - }) - setAssets(assetList) - }, [data, loading]) + return () => { + clearInterval(dataFetchInterval) + } + }, [dataFetchInterval]) - return ( + useEffect(() => { + async function getShares() { + const assetList: Asset[] = [] + const source = axios.CancelToken.source() + + try { + setLoading(true) + if (!data) { + await fetchPoolSharesData() + return + } + for (let i = 0; i < data.length; i++) { + const did = web3.utils + .toChecksumAddress(data[i].poolId.datatokenAddress) + .replace('0x', 'did:op:') + const ddo = await retrieveDDO(did, source.token) + const userLiquidity = calculateUserLiquidity(data[i]) + assetList.push({ + poolShare: data[i], + userLiquidity: userLiquidity, + networkId: ddo.chainId, + createTime: data[i].poolId.createTime + }) + } + const orderedAssets = assetList.sort( + (a, b) => b.createTime - a.createTime + ) + setAssets(orderedAssets) + refetchPoolShares() + } catch (error) { + console.error('Error fetching pool shares: ', error.message) + } finally { + setLoading(false) + } + } + + getShares() + }, [accountId, chainIds, data]) + + return accountId ? ( <Table columns={columns} className={styles.poolSharesTable} @@ -172,5 +246,7 @@ export default function PoolShares(): ReactElement { sortField="userLiquidity" sortAsc={false} /> + ) : ( + <div>Please connect your Web3 wallet.</div> ) } diff --git a/src/components/pages/History/PublishedList.tsx b/src/components/pages/History/PublishedList.tsx index 49f7a688a..536f161ad 100644 --- a/src/components/pages/History/PublishedList.tsx +++ b/src/components/pages/History/PublishedList.tsx @@ -3,20 +3,23 @@ import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/Metadata import React, { ReactElement, useEffect, useState } from 'react' import AssetList from '../../organisms/AssetList' import axios from 'axios' -import { queryMetadata } from '../../../utils/aquarius' +import { + queryMetadata, + transformChainIdsListToQuery +} from '../../../utils/aquarius' import { useWeb3 } from '../../../providers/Web3' -import { useOcean } from '../../../providers/Ocean' +import { useSiteMetadata } from '../../../hooks/useSiteMetadata' +import { useUserPreferences } from '../../../providers/UserPreferences' export default function PublishedList(): ReactElement { const { accountId } = useWeb3() - const { config } = useOcean() + const { appConfig } = useSiteMetadata() + const { chainIds } = useUserPreferences() const [queryResult, setQueryResult] = useState<QueryResult>() const [isLoading, setIsLoading] = useState(false) const [page, setPage] = useState<number>(1) - const source = axios.CancelToken.source() - useEffect(() => { async function getPublished() { if (!accountId) return @@ -25,18 +28,18 @@ export default function PublishedList(): ReactElement { offset: 9, query: { query_string: { - query: `(publicKey.owner:${accountId})` + query: `(publicKey.owner:${accountId}) AND (${transformChainIdsListToQuery( + chainIds + )})` } }, sort: { created: -1 } } try { + const source = axios.CancelToken.source() + queryResult || setIsLoading(true) - const result = await queryMetadata( - queryPublishedAssets, - config.metadataCacheUri, - source.token - ) + const result = await queryMetadata(queryPublishedAssets, source.token) setQueryResult(result) } catch (error) { Logger.error(error.message) @@ -45,7 +48,7 @@ export default function PublishedList(): ReactElement { } } getPublished() - }, [accountId, page, config.metadataCacheUri]) + }, [accountId, page, appConfig.metadataCacheUri, chainIds]) return accountId ? ( <AssetList @@ -59,6 +62,6 @@ export default function PublishedList(): ReactElement { }} /> ) : ( - <div>Connect your wallet to see your published data sets.</div> + <div>Please connect your Web3 wallet.</div> ) } diff --git a/src/components/pages/History/index.tsx b/src/components/pages/History/index.tsx index 7a9c47b94..aa0cfe2b8 100644 --- a/src/components/pages/History/index.tsx +++ b/src/components/pages/History/index.tsx @@ -6,6 +6,8 @@ import PublishedList from './PublishedList' import Downloads from './Downloads' import ComputeJobs from './ComputeJobs' import styles from './index.module.css' +import { useUserPreferences } from '../../../providers/UserPreferences' +import OceanProvider from '../../../providers/Ocean' const tabs = [ { @@ -26,11 +28,16 @@ const tabs = [ }, { title: 'Compute Jobs', - content: <ComputeJobs /> + content: ( + <OceanProvider> + <ComputeJobs /> + </OceanProvider> + ) } ] export default function HistoryPage(): ReactElement { + const { chainIds } = useUserPreferences() const url = new URL(window.location.href) const defaultTab = url.searchParams.get('defaultTab') let defaultTabIndex = 0 diff --git a/src/components/pages/Home.tsx b/src/components/pages/Home.tsx index c35921867..2b3019a62 100644 --- a/src/components/pages/Home.tsx +++ b/src/components/pages/Home.tsx @@ -7,26 +7,52 @@ import { SearchQuery } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache' import Container from '../atoms/Container' -import { useOcean } from '../../providers/Ocean' import Button from '../atoms/Button' import Bookmarks from '../molecules/Bookmarks' import axios from 'axios' -import { queryMetadata } from '../../utils/aquarius' +import { + queryMetadata, + transformChainIdsListToQuery +} from '../../utils/aquarius' import Permission from '../organisms/Permission' import { getHighestLiquidityDIDs } from '../../utils/subgraph' import { DDO, Logger } from '@oceanprotocol/lib' +import { useSiteMetadata } from '../../hooks/useSiteMetadata' +import { useUserPreferences } from '../../providers/UserPreferences' -import { useWeb3 } from '../../providers/Web3' - -const queryLatest = { - page: 1, - offset: 9, - query: { - query_string: { - query: `-isInPurgatory:true` +async function getQueryHighest( + chainIds: number[] +): Promise<[SearchQuery, string]> { + const dids = await getHighestLiquidityDIDs(chainIds) + const queryHighest = { + page: 1, + offset: dids.length, + query: { + query_string: { + query: `(${dids}) AND (${transformChainIdsListToQuery( + chainIds + )}) AND -isInPurgatory:true`, + fields: ['dataToken'] + } } - }, - sort: { created: -1 } + } + + return [queryHighest, dids] +} + +function getQueryLatest(chainIds: number[]): SearchQuery { + return { + page: 1, + offset: 9, + query: { + query_string: { + query: `(${transformChainIdsListToQuery( + chainIds + )}) AND -isInPurgatory:true ` + } + }, + sort: { created: -1 } + } } function sortElements(items: DDO[], sorted: string[]) { @@ -47,28 +73,22 @@ function SectionQueryResult({ action?: ReactElement queryData?: string }) { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const [result, setResult] = useState<QueryResult>() const [loading, setLoading] = useState<boolean>() + const { chainIds } = useUserPreferences() useEffect(() => { - if (!config?.metadataCacheUri) return + if (!appConfig.metadataCacheUri) return const source = axios.CancelToken.source() async function init() { try { setLoading(true) - const result = await queryMetadata( - query, - config.metadataCacheUri, - source.token - ) - if (queryData && result.totalResults > 0 && result.totalResults <= 15) { + const result = await queryMetadata(query, source.token) + if (queryData && result.totalResults > 0) { const searchDIDs = queryData.split(' ') const sortedAssets = sortElements(result.results, searchDIDs) - // We take more assets than we need from the subgraph (to make sure - // all the 9 assets with highest liquidity we need are in OceanDB) - // so we need to get rid of the surplus const overflow = sortedAssets.length - 9 sortedAssets.splice(sortedAssets.length - overflow, overflow) result.results = sortedAssets @@ -84,7 +104,7 @@ function SectionQueryResult({ return () => { source.cancel() } - }, [query, config?.metadataCacheUri]) + }, [appConfig.metadataCacheUri, query, queryData]) return ( <section className={styles.section}> @@ -100,34 +120,18 @@ function SectionQueryResult({ } export default function HomePage(): ReactElement { - const { config, loading } = useOcean() const [queryAndDids, setQueryAndDids] = useState<[SearchQuery, string]>() - const { web3Loading, web3Provider } = useWeb3() + const { chainIds } = useUserPreferences() useEffect(() => { - if (loading || (web3Loading && web3Provider)) return - getHighestLiquidityDIDs().then((results) => { - const queryHighest = { - page: 1, - offset: 15, - query: { - query_string: { - query: `(${results}) AND -isInPurgatory:true`, - fields: ['dataToken'] - } - } - } - setQueryAndDids([queryHighest, results]) + getQueryHighest(chainIds).then((results) => { + setQueryAndDids(results) }) - }, [config.subgraphUri, loading, web3Loading, web3Provider]) + }, [chainIds]) return ( <Permission eventType="browse"> <> - <Container narrow className={styles.searchWrap}> - <SearchBar size="large" /> - </Container> - <section className={styles.section}> <h3>Bookmarks</h3> <Bookmarks /> @@ -143,7 +147,7 @@ export default function HomePage(): ReactElement { <SectionQueryResult title="Recently Published" - query={queryLatest} + query={getQueryLatest(chainIds)} action={ <Button style="text" to="/search?sort=created&sortOrder=desc"> All data sets and algorithms → diff --git a/src/components/pages/Publish/FormActions.module.css b/src/components/pages/Publish/FormActions.module.css new file mode 100644 index 000000000..8553be147 --- /dev/null +++ b/src/components/pages/Publish/FormActions.module.css @@ -0,0 +1,5 @@ +.actions { + display: flex; + justify-content: space-between; + align-items: center; +} diff --git a/src/components/pages/Publish/FormActions.tsx b/src/components/pages/Publish/FormActions.tsx new file mode 100644 index 000000000..de500a21b --- /dev/null +++ b/src/components/pages/Publish/FormActions.tsx @@ -0,0 +1,32 @@ +import React, { FormEvent, ReactElement } from 'react' +import { useOcean } from '../../../providers/Ocean' +import Button from '../../atoms/Button' +import styles from './FormActions.module.css' + +export default function FormActions({ + isValid, + resetFormAndClearStorage +}: { + isValid: boolean + resetFormAndClearStorage: (e: FormEvent<Element>) => void +}): ReactElement { + const { ocean, account } = useOcean() + + return ( + <footer className={styles.actions}> + <Button + style="primary" + type="submit" + disabled={!ocean || !account || !isValid || status === 'empty'} + > + Submit + </Button> + + {status !== 'empty' && ( + <Button style="text" size="small" onClick={resetFormAndClearStorage}> + Reset Form + </Button> + )} + </footer> + ) +} diff --git a/src/components/pages/Publish/FormAlgoPublish.tsx b/src/components/pages/Publish/FormAlgoPublish.tsx index f5db86d3f..3d60937f8 100644 --- a/src/components/pages/Publish/FormAlgoPublish.tsx +++ b/src/components/pages/Publish/FormAlgoPublish.tsx @@ -6,15 +6,14 @@ import React, { ChangeEvent } from 'react' import { useStaticQuery, graphql } from 'gatsby' -import styles from './FormPublish.module.css' -import { useOcean } from '../../../providers/Ocean' import { useFormikContext, Field, Form, FormikContextType } from 'formik' import Input from '../../atoms/Input' -import Button from '../../atoms/Button' import { FormContent, FormFieldProps } from '../../../@types/Form' import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData' import { initialValues as initialValuesAlgorithm } from '../../../models/FormAlgoPublish' -import stylesIndex from './index.module.css' +import FormTitle from './FormTitle' +import FormActions from './FormActions' +import styles from './FormPublish.module.css' const query = graphql` query { @@ -46,7 +45,7 @@ const query = graphql` export default function FormPublish(): ReactElement { const data = useStaticQuery(query) const content: FormContent = data.content.edges[0].node.childPublishJson - const { ocean, account } = useOcean() + const { status, setStatus, @@ -142,7 +141,8 @@ export default function FormPublish(): ReactElement { // do we need this? onChange={() => status === 'empty' && setStatus(null)} > - <h2 className={stylesIndex.formTitle}>{content.title}</h2> + <FormTitle title={content.title} /> + {content.data.map( (field: FormFieldProps) => ((field.name !== 'entrypoint' && @@ -165,21 +165,10 @@ export default function FormPublish(): ReactElement { ) )} - <footer className={styles.actions}> - <Button - style="primary" - type="submit" - disabled={!ocean || !account || !isValid || status === 'empty'} - > - Submit - </Button> - - {status !== 'empty' && ( - <Button style="text" size="small" onClick={resetFormAndClearStorage}> - Reset Form - </Button> - )} - </footer> + <FormActions + isValid={isValid} + resetFormAndClearStorage={resetFormAndClearStorage} + /> </Form> ) } diff --git a/src/components/pages/Publish/FormPublish.module.css b/src/components/pages/Publish/FormPublish.module.css index 6b4b09367..59f56e9e8 100644 --- a/src/components/pages/Publish/FormPublish.module.css +++ b/src/components/pages/Publish/FormPublish.module.css @@ -5,9 +5,3 @@ border-top-left-radius: 0; border-top-right-radius: 0; } - -.actions { - display: flex; - justify-content: space-between; - align-items: center; -} diff --git a/src/components/pages/Publish/FormPublish.tsx b/src/components/pages/Publish/FormPublish.tsx index 96dd6be1f..b11039cfd 100644 --- a/src/components/pages/Publish/FormPublish.tsx +++ b/src/components/pages/Publish/FormPublish.tsx @@ -2,14 +2,14 @@ import React, { ReactElement, useEffect, FormEvent, ChangeEvent } from 'react' import { useStaticQuery, graphql } from 'gatsby' import { useFormikContext, Field, Form, FormikContextType } from 'formik' import Input from '../../atoms/Input' -import Button from '../../atoms/Button' import { FormContent, FormFieldProps } from '../../../@types/Form' import { MetadataPublishFormDataset } from '../../../@types/MetaData' import { initialValues as initialValuesDataset } from '../../../models/FormAlgoPublish' import { useOcean } from '../../../providers/Ocean' import { ReactComponent as Download } from '../../../images/download.svg' import { ReactComponent as Compute } from '../../../images/compute.svg' -import stylesIndex from './index.module.css' +import FormTitle from './FormTitle' +import FormActions from './FormActions' import styles from './FormPublish.module.css' const query = graphql` @@ -42,6 +42,7 @@ const query = graphql` export default function FormPublish(): ReactElement { const data = useStaticQuery(query) const content: FormContent = data.content.edges[0].node.childPublishJson + const { ocean, account } = useOcean() const { status, @@ -50,7 +51,6 @@ export default function FormPublish(): ReactElement { setErrors, setTouched, resetForm, - initialValues, validateField, setFieldValue }: FormikContextType<MetadataPublishFormDataset> = useFormikContext() @@ -104,7 +104,8 @@ export default function FormPublish(): ReactElement { // do we need this? onChange={() => status === 'empty' && setStatus(null)} > - <h2 className={stylesIndex.formTitle}>{content.title}</h2> + <FormTitle title={content.title} /> + {content.data.map((field: FormFieldProps) => ( <Field key={field.name} @@ -119,21 +120,10 @@ export default function FormPublish(): ReactElement { /> ))} - <footer className={styles.actions}> - <Button - style="primary" - type="submit" - disabled={!ocean || !account || !isValid || status === 'empty'} - > - Submit - </Button> - - {status !== 'empty' && ( - <Button style="text" size="small" onClick={resetFormAndClearStorage}> - Reset Form - </Button> - )} - </footer> + <FormActions + isValid={isValid} + resetFormAndClearStorage={resetFormAndClearStorage} + /> </Form> ) } diff --git a/src/components/pages/Publish/FormTitle.module.css b/src/components/pages/Publish/FormTitle.module.css new file mode 100644 index 000000000..9ff189d87 --- /dev/null +++ b/src/components/pages/Publish/FormTitle.module.css @@ -0,0 +1,21 @@ +.title { + font-size: var(--font-size-h4); + display: inline-flex; +} + +.network { + color: var(--font-color-heading); + margin-left: calc(var(--spacer) / 8); +} + +.network svg { + width: 1em; + height: 1em; + margin-top: -0.25em; + fill: var(--color-secondary); +} + +.tooltip { + width: 0.75em; + height: 0.75em; +} diff --git a/src/components/pages/Publish/FormTitle.tsx b/src/components/pages/Publish/FormTitle.tsx new file mode 100644 index 000000000..9b5c775b6 --- /dev/null +++ b/src/components/pages/Publish/FormTitle.tsx @@ -0,0 +1,42 @@ +import React, { ReactElement } from 'react' +import NetworkName from '../../atoms/NetworkName' +import Tooltip from '../../atoms/Tooltip' +import { useWeb3 } from '../../../providers/Web3' +import styles from './FormTitle.module.css' + +import { graphql, useStaticQuery } from 'gatsby' + +const query = graphql` + query { + content: allFile( + filter: { relativePath: { eq: "pages/publish/index.json" } } + ) { + edges { + node { + childPublishJson { + tooltipNetwork + } + } + } + } + } +` + +export default function FormTitle({ title }: { title: string }): ReactElement { + const data = useStaticQuery(query) + const contentTooltip = + data.content.edges[0].node.childPublishJson.tooltipNetwork + const { networkId } = useWeb3() + + return ( + <h2 className={styles.title}> + {title}{' '} + {networkId && ( + <> + into <NetworkName networkId={networkId} className={styles.network} /> + <Tooltip content={contentTooltip} className={styles.tooltip} /> + </> + )} + </h2> + ) +} diff --git a/src/components/pages/Publish/index.module.css b/src/components/pages/Publish/index.module.css index fe63517cf..501d995b7 100644 --- a/src/components/pages/Publish/index.module.css +++ b/src/components/pages/Publish/index.module.css @@ -39,7 +39,3 @@ div.alert { top: calc(var(--spacer) / 2); } } - -.formTitle { - font-size: var(--font-size-h4); -} diff --git a/src/components/pages/Publish/index.tsx b/src/components/pages/Publish/index.tsx index c27dcd3b6..79add849a 100644 --- a/src/components/pages/Publish/index.tsx +++ b/src/components/pages/Publish/index.tsx @@ -5,7 +5,7 @@ import { usePublish } from '../../../hooks/usePublish' import styles from './index.module.css' import FormPublish from './FormPublish' import FormAlgoPublish from './FormAlgoPublish' -import Web3Feedback from '../../molecules/Wallet/Feedback' +import Web3Feedback from '../../molecules/Web3Feedback' import Tabs from '../../atoms/Tabs' import { initialValues, validationSchema } from '../../../models/FormPublish' import { @@ -56,7 +56,6 @@ function TabContent({ ) : ( <MetadataAlgorithmPreview values={values} /> )} - <Web3Feedback /> </div> </aside> diff --git a/src/components/templates/PageAssetDetails.tsx b/src/components/templates/PageAssetDetails.tsx index a48361210..1c125878d 100644 --- a/src/components/templates/PageAssetDetails.tsx +++ b/src/components/templates/PageAssetDetails.tsx @@ -24,13 +24,11 @@ export default function PageTemplateAssetDetails({ }, [ddo, error, isInPurgatory, title]) return ddo && pageTitle !== undefined && !loading ? ( - <> - <Page title={pageTitle} uri={uri}> - <Router basepath="/asset"> - <AssetContent path=":did" /> - </Router> - </Page> - </> + <Page title={pageTitle} uri={uri}> + <Router basepath="/asset"> + <AssetContent path=":did" /> + </Router> + </Page> ) : error ? ( <Page title={pageTitle} noPageHeader uri={uri}> <Alert title={pageTitle} text={error} state="error" /> diff --git a/src/components/templates/Search/index.tsx b/src/components/templates/Search/index.tsx index a4d581453..2f9508b41 100644 --- a/src/components/templates/Search/index.tsx +++ b/src/components/templates/Search/index.tsx @@ -1,7 +1,6 @@ import React, { ReactElement, useState, useEffect } from 'react' import Permission from '../../organisms/Permission' import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache' -import SearchBar from '../../molecules/SearchBar' import AssetList from '../../organisms/AssetList' import styles from './index.module.css' import queryString from 'query-string' @@ -10,7 +9,8 @@ import Sort from './sort' import { getResults } from './utils' import { navigate } from 'gatsby' import { updateQueryStringParameter } from '../../../utils' -import { useOcean } from '../../../providers/Ocean' +import { useSiteMetadata } from '../../../hooks/useSiteMetadata' +import { useUserPreferences } from '../../../providers/UserPreferences' export default function SearchPage({ location, @@ -19,9 +19,10 @@ export default function SearchPage({ location: Location setTotalResults: (totalResults: number) => void }): ReactElement { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() const parsed = queryString.parse(location.search) const { text, owner, tags, page, sort, sortOrder, serviceType } = parsed + const { chainIds } = useUserPreferences() const [queryResult, setQueryResult] = useState<QueryResult>() const [loading, setLoading] = useState<boolean>() const [service, setServiceType] = useState<string>(serviceType as string) @@ -31,11 +32,15 @@ export default function SearchPage({ ) useEffect(() => { - if (!config?.metadataCacheUri) return + if (!appConfig.metadataCacheUri) return async function initSearch() { setLoading(true) setTotalResults(undefined) - const queryResult = await getResults(parsed, config.metadataCacheUri) + const queryResult = await getResults( + parsed, + appConfig.metadataCacheUri, + chainIds + ) setQueryResult(queryResult) setTotalResults(queryResult.totalResults) setLoading(false) @@ -49,7 +54,8 @@ export default function SearchPage({ page, serviceType, sortOrder, - config.metadataCacheUri + appConfig.metadataCacheUri, + chainIds ]) function setPage(page: number) { @@ -65,9 +71,6 @@ export default function SearchPage({ <Permission eventType="browse"> <> <div className={styles.search}> - {(text || owner || tags) && ( - <SearchBar initialValue={(text || owner) as string} /> - )} <div className={styles.row}> <ServiceFilter serviceType={service} diff --git a/src/components/templates/Search/utils.ts b/src/components/templates/Search/utils.ts index 2d4cfb9e1..41cc1ac55 100644 --- a/src/components/templates/Search/utils.ts +++ b/src/components/templates/Search/utils.ts @@ -1,6 +1,11 @@ import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache' import { MetadataCache, Logger } from '@oceanprotocol/lib' +import { + queryMetadata, + transformChainIdsListToQuery +} from '../../../utils/aquarius' import queryString from 'query-string' +import axios from 'axios' export const SortTermOptions = { Created: 'created', @@ -46,6 +51,7 @@ function getSortType(sortParam: string): string { } export function getSearchQuery( + chainIds: number[], text?: string, owner?: string, tags?: string, @@ -137,6 +143,11 @@ export function getSearchQuery( ] } }, + { + query_string: { + query: `${transformChainIdsListToQuery(chainIds)}` + } + }, { term: { isInPurgatory: false @@ -163,7 +174,8 @@ export async function getResults( sortOrder?: string serviceType?: string }, - metadataCacheUri: string + metadataCacheUri: string, + chainIds: number[] ): Promise<QueryResult> { const { text, @@ -176,9 +188,9 @@ export async function getResults( sortOrder, serviceType } = params - const metadataCache = new MetadataCache(metadataCacheUri, Logger) const searchQuery = getSearchQuery( + chainIds, text, owner, tags, @@ -189,7 +201,9 @@ export async function getResults( sortOrder, serviceType ) - const queryResult = await metadataCache.queryMetadata(searchQuery) + const source = axios.CancelToken.source() + // const queryResult = await metadataCache.queryMetadata(searchQuery) + const queryResult = await queryMetadata(searchQuery, source.token) return queryResult } diff --git a/src/helpers/wrapRootElement.tsx b/src/helpers/wrapRootElement.tsx index 84e22110a..6fe5c5774 100644 --- a/src/helpers/wrapRootElement.tsx +++ b/src/helpers/wrapRootElement.tsx @@ -1,36 +1,21 @@ import React, { ReactElement } from 'react' import Web3Provider from '../providers/Web3' -import appConfig from '../../app.config' import { UserPreferencesProvider } from '../providers/UserPreferences' import PricesProvider from '../providers/Prices' -import ApolloClientProvider from '../providers/ApolloClientProvider' -import OceanProvider from '../providers/Ocean' -import { getDevelopmentConfig, getOceanConfig } from '../utils/ocean' +import UrqlProvider from '../providers/UrqlProvider' export default function wrapRootElement({ element }: { element: ReactElement }): ReactElement { - const { network } = appConfig - const oceanInitialConfig = { - ...getOceanConfig(network), - - // add local dev values - ...(network === 'development' && { - ...getDevelopmentConfig() - }) - } - return ( <Web3Provider> - <OceanProvider initialConfig={oceanInitialConfig}> - <ApolloClientProvider> - <UserPreferencesProvider> - <PricesProvider>{element}</PricesProvider> - </UserPreferencesProvider> - </ApolloClientProvider> - </OceanProvider> + <UrqlProvider> + <UserPreferencesProvider> + <PricesProvider>{element}</PricesProvider> + </UserPreferencesProvider> + </UrqlProvider> </Web3Provider> ) } diff --git a/src/hooks/useGraphSyncStatus.ts b/src/hooks/useGraphSyncStatus.ts index acc2036e2..5252f0839 100644 --- a/src/hooks/useGraphSyncStatus.ts +++ b/src/hooks/useGraphSyncStatus.ts @@ -64,7 +64,7 @@ export function useGraphSyncStatus(): UseGraphSyncStatus { // Get and set head block useEffect(() => { - if (!config || !config.nodeUri || web3Loading) return + if (!config?.nodeUri || web3Loading) return async function initBlockHead() { const blockHead = block || (await getBlockHead(config)) @@ -72,11 +72,11 @@ export function useGraphSyncStatus(): UseGraphSyncStatus { Logger.log('[GraphStatus] Head block: ', blockHead) } initBlockHead() - }, [web3Loading, block, config.nodeUri]) + }, [web3Loading, block, config]) // Get and set subgraph block useEffect(() => { - if (!config || !config.subgraphUri) return + if (!config?.subgraphUri) return async function initBlockSubgraph() { setSubgraphLoading(true) @@ -86,7 +86,7 @@ export function useGraphSyncStatus(): UseGraphSyncStatus { Logger.log('[GraphStatus] Latest block from subgraph: ', blockGraph) } initBlockSubgraph() - }, [config.subgraphUri]) + }, [config]) // Set sync status useEffect(() => { diff --git a/src/hooks/useNetworkMetadata.ts b/src/hooks/useNetworkMetadata.ts new file mode 100644 index 000000000..78bd9b3bd --- /dev/null +++ b/src/hooks/useNetworkMetadata.ts @@ -0,0 +1,34 @@ +import { useStaticQuery, graphql } from 'gatsby' +import { EthereumListsChain } from '../utils/web3' + +export interface UseNetworkMetadata { + networksList: { node: EthereumListsChain }[] +} + +const networksQuery = graphql` + query { + allNetworksMetadataJson { + edges { + node { + chain + network + networkId + chainId + nativeCurrency { + name + symbol + decimals + } + } + } + } + } +` + +export default function useNetworkMetadata(): UseNetworkMetadata { + const data = useStaticQuery(networksQuery) + const networksList: { node: EthereumListsChain }[] = + data.allNetworksMetadataJson.edges + + return { networksList } +} diff --git a/src/hooks/useSiteMetadata.ts b/src/hooks/useSiteMetadata.ts index b33ed34b1..c8e310078 100644 --- a/src/hooks/useSiteMetadata.ts +++ b/src/hooks/useSiteMetadata.ts @@ -20,8 +20,10 @@ interface UseSiteMetadata { polygon: string } appConfig: { + metadataCacheUri: string infuraProjectId: string - network: string + chainIds: number[] + chainIdsSupported: number[] marketFeeAddress: string currencies: string[] portisId: string @@ -55,8 +57,10 @@ const query = graphql` polygon } appConfig { + metadataCacheUri infuraProjectId - network + chainIds + chainIdsSupported marketFeeAddress currencies portisId diff --git a/src/images/bsc.svg b/src/images/bsc.svg new file mode 100644 index 000000000..8392b72be --- /dev/null +++ b/src/images/bsc.svg @@ -0,0 +1,7 @@ +<svg width="17" height="17" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg"> +<path d="M5.19853 7.14354L8.4999 3.84226L11.803 7.14529L13.724 5.22429L8.4999 0L3.27757 5.22255L5.19853 7.14354Z" /> +<path d="M3.84196 8.49969L1.92103 6.57873L0 8.49979L1.92093 10.4207L3.84196 8.49969Z" /> +<path d="M5.19854 9.85646L8.49991 13.1577L11.8029 9.85485L13.7249 11.7748L13.724 11.7758L8.49991 17L3.27475 11.7749L5.19854 9.85646Z" /> +<path d="M15.079 10.4217L17 8.50061L15.0791 6.57965L13.158 8.50071L15.079 10.4217Z" /> +<path d="M10.4484 8.499H10.4492L8.4999 6.54967L6.54967 8.49994L6.55235 8.50276L8.4999 10.4503L10.4492 8.50101L10.4501 8.49994L10.4484 8.499Z" /> +</svg> diff --git a/src/images/eth.svg b/src/images/eth.svg new file mode 100644 index 000000000..6abd31517 --- /dev/null +++ b/src/images/eth.svg @@ -0,0 +1,8 @@ +<svg width="13" height="22" viewBox="0 0 13 22" xmlns="http://www.w3.org/2000/svg"> +<path d="M6.89184 5.43594e-05L12.8197 9.94919L6.91187 7.38561L6.89184 5.43594e-05Z"/> +<path d="M0 9.94919L5.91188 5.43594e-05L5.93191 7.38561L0 9.94919Z" /> +<path d="M5.91189 14.7089L1.43051e-05 11.0385L5.93192 8.47489L5.91189 14.7089Z" /> +<path d="M12.8198 11.0385L6.91188 8.47489L6.89185 14.7089L12.8198 11.0385Z" /> +<path d="M6.91188 16.2936L12.8398 12.725L6.91188 21.2092V16.2936Z" /> +<path d="M5.91188 16.2936L0 12.725L5.91188 21.2092V16.2936Z" /> +</svg> diff --git a/src/images/moonbeam.svg b/src/images/moonbeam.svg new file mode 100644 index 000000000..b649d23f1 --- /dev/null +++ b/src/images/moonbeam.svg @@ -0,0 +1,16 @@ +<svg width="20" height="17" viewBox="0 0 20 17" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.9072 0C10.2486 0.000152742 8.65813 0.656483 7.48551 1.82455C6.31279 2.99267 5.65395 4.5769 5.65379 6.22891V6.25182C5.6589 6.32956 5.69361 6.40247 5.75076 6.45563C5.80795 6.50888 5.88329 6.53846 5.96149 6.53826H17.8516C17.9303 6.53846 18.0054 6.50888 18.0626 6.45563C18.1199 6.40247 18.1541 6.32956 18.1598 6.25182V6.24204C18.1598 6.23766 18.1598 6.23323 18.1598 6.22891C18.1592 4.57701 17.5004 2.99287 16.3279 1.8248C15.1554 0.656636 13.5653 0.000305483 11.9072 0Z" /> +<path d="M1.13367 13.563C1.34395 13.563 1.51441 13.3932 1.51441 13.1837C1.51441 12.9742 1.34395 12.8044 1.13367 12.8044C0.923397 12.8044 0.752939 12.9742 0.752939 13.1837C0.752939 13.3932 0.923397 13.563 1.13367 13.563Z" /> +<path d="M17.4391 11.0856H4.68072C4.61586 11.0856 4.55207 11.1024 4.49564 11.1343C4.43921 11.1662 4.39214 11.2121 4.35881 11.2676C4.32559 11.3231 4.30734 11.3862 4.30586 11.4508C4.30443 11.5154 4.31981 11.5793 4.35058 11.6362L4.35749 11.6482C4.38958 11.7073 4.43722 11.7567 4.49523 11.7911C4.55324 11.8255 4.61954 11.8436 4.68701 11.8436H17.4319C17.4994 11.8436 17.5658 11.8254 17.6241 11.791C17.6819 11.7567 17.7299 11.7073 17.7621 11.6482L17.7688 11.6362C17.7994 11.5793 17.8148 11.5155 17.8132 11.4509C17.8117 11.3864 17.7938 11.3233 17.7606 11.2679C17.7273 11.2124 17.6803 11.1664 17.6236 11.1345C17.5674 11.1025 17.504 11.0857 17.4391 11.0856Z" /> +<path d="M19.134 7.64803H4.6801C4.62909 7.64823 4.57865 7.65872 4.53183 7.67893C4.48501 7.69915 4.44284 7.72868 4.40788 7.76559C4.37292 7.8026 4.34583 7.84634 4.3283 7.89404C4.31087 7.94185 4.30336 7.99261 4.30622 8.04338V8.05483C4.31153 8.14999 4.35329 8.23949 4.42281 8.30492C4.49237 8.37034 4.58442 8.40675 4.6801 8.40659H19.1329C19.228 8.40675 19.321 8.37039 19.3905 8.30497C19.4601 8.23955 19.502 8.15004 19.5071 8.05483V8.04338C19.5101 7.99272 19.5025 7.94195 19.4846 7.8943C19.4672 7.84659 19.4406 7.80296 19.4054 7.766C19.3706 7.72903 19.3287 7.69955 19.2817 7.67924C19.2352 7.65897 19.1851 7.64833 19.134 7.64803Z" /> +<path d="M13.6915 16.242H7.17622C6.78112 16.242 6.65861 16.772 7.00945 16.9495L7.03301 16.9616C7.08509 16.9872 7.14244 17.0003 7.2004 17H13.6675C13.7258 17.0003 13.783 16.9872 13.8346 16.9616L13.8587 16.9495C14.2118 16.7714 14.0866 16.242 13.6915 16.242Z" /> +<path d="M17.6711 14.5231H11.1575C10.7618 14.5231 10.6399 15.0531 10.9907 15.2307L11.0143 15.2428C11.0663 15.2683 11.1236 15.2814 11.1816 15.2811H17.6476C17.7054 15.2813 17.7626 15.2683 17.8142 15.2428L17.8388 15.2307C17.9139 15.1931 17.9747 15.1311 18.0105 15.055C18.0463 14.9789 18.055 14.8929 18.0356 14.8111C18.0161 14.7292 17.9696 14.6562 17.9037 14.604C17.8378 14.5517 17.7555 14.5233 17.6711 14.5231Z" /> +<path d="M8.78969 13.4438L8.77819 13.4318C8.72835 13.3786 8.69523 13.312 8.68291 13.2403C8.67065 13.1686 8.67959 13.0948 8.70883 13.0283C8.73807 12.9616 8.78621 12.9048 8.84739 12.8651C8.90858 12.8253 8.98008 12.8042 9.05312 12.8044H19.6251C19.6982 12.8043 19.7693 12.8255 19.8306 12.8652C19.8914 12.9049 19.9395 12.9616 19.9691 13.0281C19.9978 13.0946 20.007 13.1683 19.9947 13.2399C19.9829 13.3115 19.9497 13.378 19.9001 13.4312L19.8889 13.4433C19.8541 13.4808 19.8112 13.5107 19.7642 13.5311C19.7166 13.5516 19.666 13.5623 19.6139 13.5624H9.06406C9.01254 13.5624 8.96173 13.5519 8.91451 13.5314C8.86733 13.5111 8.82485 13.4812 8.78969 13.4438Z" /> +<path d="M2.49275 12.8044H7.54153C7.93724 12.8044 8.05914 13.3344 7.70831 13.512L7.68474 13.5234C7.63271 13.5492 7.57547 13.5625 7.51735 13.5624H2.51749C2.45943 13.5625 2.40213 13.5492 2.3501 13.5234L2.32653 13.512C1.97279 13.335 2.09704 12.8044 2.49275 12.8044Z" /> +<path d="M3.32098 8.40833C3.53125 8.40833 3.70176 8.23853 3.70176 8.02907C3.70176 7.81961 3.53125 7.64976 3.32098 7.64976C3.11065 7.64976 2.94024 7.81961 2.94024 8.02907C2.94024 8.23853 3.11065 8.40833 3.32098 8.40833Z" /> +<path d="M12.6723 9.8499L12.6759 9.83784C12.6907 9.78254 12.6928 9.72465 12.6815 9.66849C12.6703 9.61234 12.6458 9.55959 12.611 9.51417C12.5757 9.46881 12.5308 9.4321 12.4791 9.4069C12.4275 9.38165 12.3708 9.36856 12.3135 9.36861H1.74038C1.68293 9.36856 1.62624 9.38165 1.57467 9.4069C1.5231 9.4321 1.47807 9.46881 1.44306 9.51417C1.408 9.55959 1.38392 9.61234 1.37262 9.66849C1.36138 9.72465 1.36322 9.78254 1.37804 9.83784L1.38152 9.8499C1.40294 9.92917 1.45001 9.99923 1.51538 10.0492C1.58081 10.0992 1.66085 10.1264 1.74329 10.1266H12.31C12.3928 10.1264 12.4725 10.0992 12.5379 10.0491C12.6039 9.99918 12.6509 9.92917 12.6723 9.8499Z" /> +<path d="M0.380788 10.1266C0.591116 10.1266 0.761526 9.95672 0.761526 9.74726C0.761526 9.53775 0.591116 9.368 0.380788 9.368C0.170512 9.368 0 9.53775 0 9.74726C0 9.95672 0.170512 10.1266 0.380788 10.1266Z" /> +<path d="M3.32098 11.8448C3.53125 11.8448 3.70176 11.675 3.70176 11.4655C3.70176 11.256 3.53125 11.0862 3.32098 11.0862C3.11065 11.0862 2.94024 11.256 2.94024 11.4655C2.94024 11.675 3.11065 11.8448 3.32098 11.8448Z" /> +<path d="M9.79849 15.2817C10.0088 15.2817 10.1793 15.1119 10.1793 14.9024C10.1793 14.693 10.0088 14.5231 9.79849 14.5231C9.58827 14.5231 9.41776 14.693 9.41776 14.9024C9.41776 15.1119 9.58827 15.2817 9.79849 15.2817Z" /> +<path d="M5.81832 16.9999C6.0286 16.9999 6.19906 16.8301 6.19906 16.6206C6.19906 16.4112 6.0286 16.2414 5.81832 16.2414C5.608 16.2414 5.43759 16.4112 5.43759 16.6206C5.43759 16.8301 5.608 16.9999 5.81832 16.9999Z" /> +</svg> diff --git a/src/images/network.svg b/src/images/network.svg new file mode 100644 index 000000000..8b547d691 --- /dev/null +++ b/src/images/network.svg @@ -0,0 +1,3 @@ +<svg width="19" height="19" viewBox="0 0 19 19" xmlns="http://www.w3.org/2000/svg"> +<path d="M4.474 6.115C4.674 6.115 4.868 6.144 5.05 6.201C5.74 5.428 6.505 4.724 7.333 4.101C7.298 3.955 7.279 3.802 7.279 3.646C7.279 3.429 7.315 3.22 7.381 3.025C6.452 2.361 5.444 1.802 4.37 1.371C3.365 1.992 2.488 2.802 1.788 3.752C2.307 4.672 2.924 5.529 3.626 6.309C3.882 6.185 4.169 6.115 4.474 6.115ZM2.516 8.072C2.516 7.797 2.574 7.535 2.675 7.299C2.047 6.607 1.478 5.863 0.98 5.07C0.355 6.312 0 7.715 0 9.201C0 10.92 0.474 12.529 1.295 13.906C1.589 12.252 2.146 10.687 2.915 9.254C2.665 8.926 2.516 8.515 2.516 8.072ZM9.236 1.689C9.753 1.689 10.221 1.89 10.572 2.218C11.593 1.779 12.668 1.441 13.787 1.226C12.436 0.447 10.87 0 9.2 0C8.061 0 6.971 0.209 5.964 0.588C6.763 0.983 7.525 1.443 8.241 1.963C8.533 1.789 8.872 1.689 9.236 1.689ZM12.162 10.908C12.284 10.654 12.457 10.429 12.671 10.252C12.093 8.475 11.178 6.848 9.999 5.449C9.765 5.549 9.507 5.603 9.235 5.603C8.81 5.603 8.419 5.466 8.098 5.238C7.388 5.777 6.731 6.381 6.134 7.041C6.322 7.34 6.431 7.693 6.431 8.072C6.431 8.275 6.4 8.472 6.341 8.658C8.058 9.804 10.035 10.592 12.162 10.908ZM14.701 13.551C14.727 13.922 14.742 14.295 14.742 14.672C14.742 15.336 14.7 15.99 14.62 16.633C16.18 15.494 17.368 13.875 17.967 11.994C17.257 12.166 16.529 12.289 15.786 12.359C15.613 12.896 15.214 13.328 14.701 13.551ZM11.704 12.224C9.432 11.847 7.327 10.98 5.494 9.74C5.196 9.922 4.847 10.029 4.473 10.029C4.334 10.029 4.201 10.015 4.071 9.988C3.194 11.65 2.633 13.505 2.472 15.472C3.348 16.412 4.416 17.169 5.617 17.677C7.109 15.384 9.193 13.488 11.704 12.224ZM15.376 2.381C13.901 2.524 12.493 2.895 11.183 3.449C11.189 3.515 11.193 3.58 11.193 3.646C11.193 3.955 11.119 4.246 10.992 4.505C12.303 6.044 13.319 7.835 13.961 9.796C14.758 9.812 15.438 10.298 15.731 10.991C16.617 10.901 17.479 10.731 18.309 10.487C18.369 10.067 18.401 9.637 18.401 9.2C18.4 6.496 17.234 4.064 15.376 2.381ZM12.689 13.269C10.345 14.367 8.385 16.058 6.966 18.125C7.681 18.304 8.429 18.4 9.2 18.4C10.587 18.4 11.902 18.091 13.082 17.541C13.272 16.613 13.372 15.654 13.372 14.672C13.372 14.317 13.356 13.965 13.329 13.617C13.093 13.541 12.876 13.424 12.689 13.269Z" /> +</svg> diff --git a/src/images/polygon.svg b/src/images/polygon.svg new file mode 100644 index 000000000..4c041a336 --- /dev/null +++ b/src/images/polygon.svg @@ -0,0 +1,3 @@ +<svg width="20" height="17" viewBox="0 0 20 17" xmlns="http://www.w3.org/2000/svg"> +<path d="M15.0923 5.19344C14.7233 4.97781 14.2435 4.97781 13.8376 5.19344L10.9594 6.84672L9.00368 7.92496L6.12545 9.57824C5.75645 9.79387 5.27674 9.79387 4.87084 9.57824L2.58303 8.28436C2.21402 8.0687 1.95572 7.67337 1.95572 7.24208V4.69029C1.95572 4.25899 2.17712 3.86363 2.58303 3.648L4.83394 2.39006C5.20297 2.17442 5.68264 2.17442 6.08855 2.39006L8.33948 3.648C8.70848 3.86363 8.96677 4.25899 8.96677 4.69029V6.34356L10.9225 5.22938V3.57611C10.9225 3.14482 10.7011 2.74947 10.2952 2.53383L6.12545 0.161733C5.75645 -0.0539112 5.27674 -0.0539112 4.87084 0.161733L0.627306 2.53383C0.221402 2.74947 0 3.14482 0 3.57611V8.35622C0 8.78752 0.221402 9.18288 0.627306 9.39851L4.87084 11.7706C5.23984 11.9863 5.71955 11.9863 6.12545 11.7706L9.00368 10.1533L10.9594 9.03911L13.8376 7.42177C14.2066 7.20614 14.6864 7.20614 15.0923 7.42177L17.3432 8.67972C17.7122 8.89535 17.9705 9.29071 17.9705 9.722V12.2738C17.9705 12.7051 17.7491 13.1004 17.3432 13.3161L15.0923 14.6099C14.7233 14.8256 14.2435 14.8256 13.8376 14.6099L11.5867 13.352C11.2177 13.1364 10.9594 12.741 10.9594 12.3097V10.6565L9.00368 11.7706V13.4239C9.00368 13.8552 9.2251 14.2505 9.631 14.4662L13.8745 16.8383C14.2435 17.0539 14.7233 17.0539 15.1292 16.8383L19.3727 14.4662C19.7417 14.2505 20 13.8552 20 13.4239V8.64375C20 8.21246 19.7786 7.81713 19.3727 7.60147L15.0923 5.19344Z" /> +</svg> diff --git a/src/images/search.svg b/src/images/search.svg new file mode 100644 index 000000000..d06c05c4a --- /dev/null +++ b/src/images/search.svg @@ -0,0 +1,3 @@ +<svg width="19" height="19" viewBox="0 0 19 19" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5846 12.6731C10.3622 13.6572 8.81213 14.2501 7.12507 14.2501C3.19599 14.2501 0 11.0534 0 7.12507C0 3.19599 3.19599 0 7.12507 0C11.0542 0 14.2501 3.19599 14.2501 7.12507C14.2501 8.7963 13.6675 10.3329 12.6993 11.549L18.7698 17.6496C19.0778 17.9591 19.077 18.4603 18.7667 18.769C18.4555 19.0778 17.9552 19.077 17.6472 18.7667L11.5846 12.6731ZM12.6668 7.12507C12.6668 4.06921 10.1801 1.58335 7.12507 1.58335C4.06921 1.58335 1.58335 4.06921 1.58335 7.12507C1.58335 10.1801 4.06921 12.6668 7.12507 12.6668C10.1801 12.6668 12.6668 10.1801 12.6668 7.12507Z" /> +</svg> diff --git a/src/pages/asset/index.tsx b/src/pages/asset/index.tsx index 025602a3c..5d755a3f2 100644 --- a/src/pages/asset/index.tsx +++ b/src/pages/asset/index.tsx @@ -3,6 +3,7 @@ import Permission from '../../components/organisms/Permission' import { PageProps } from 'gatsby' import PageTemplateAssetDetails from '../../components/templates/PageAssetDetails' import AssetProvider from '../../providers/Asset' +import OceanProvider from '../../providers/Ocean' export default function PageGatsbyAssetDetails(props: PageProps): ReactElement { const [did, setDid] = useState<string>() @@ -14,7 +15,9 @@ export default function PageGatsbyAssetDetails(props: PageProps): ReactElement { return ( <Permission eventType="browse"> <AssetProvider asset={did}> - <PageTemplateAssetDetails uri={props.location.pathname} /> + <OceanProvider> + <PageTemplateAssetDetails uri={props.location.pathname} /> + </OceanProvider> </AssetProvider> </Permission> ) diff --git a/src/providers/ApolloClientProvider.tsx b/src/providers/ApolloClientProvider.tsx deleted file mode 100644 index ec60215ee..000000000 --- a/src/providers/ApolloClientProvider.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - ApolloClient, - ApolloProvider, - HttpLink, - InMemoryCache, - NormalizedCacheObject -} from '@apollo/client' -import { Logger, ConfigHelperConfig } from '@oceanprotocol/lib' -import { useOcean } from './Ocean' -import fetch from 'cross-fetch' -import React, { useState, useEffect, ReactNode, ReactElement } from 'react' -let apolloClient: ApolloClient<NormalizedCacheObject> - -function createClient(subgraphUri: string) { - const client = new ApolloClient({ - link: new HttpLink({ - uri: `${subgraphUri}/subgraphs/name/oceanprotocol/ocean-subgraph`, - fetch - }), - cache: new InMemoryCache() - }) - - return client -} - -export function getApolloClientInstance(): ApolloClient<NormalizedCacheObject> { - return apolloClient -} - -export default function ApolloClientProvider({ - children -}: { - children: ReactNode -}): ReactElement { - const { config } = useOcean() - const [client, setClient] = useState<ApolloClient<NormalizedCacheObject>>() - - useEffect(() => { - if (!(config as ConfigHelperConfig)?.subgraphUri) { - Logger.error( - 'No subgraphUri defined, preventing ApolloProvider from initialization.' - ) - return - } - - const newClient = createClient((config as ConfigHelperConfig).subgraphUri) - apolloClient = newClient - setClient(newClient) - }, [config]) - - return client ? ( - <ApolloProvider client={client}>{children}</ApolloProvider> - ) : ( - <></> - ) -} - -export { ApolloClientProvider } diff --git a/src/providers/Asset.tsx b/src/providers/Asset.tsx index 885fc5cde..5f6659206 100644 --- a/src/providers/Asset.tsx +++ b/src/providers/Asset.tsx @@ -14,20 +14,22 @@ import axios, { CancelToken } from 'axios' import { retrieveDDO } from '../utils/aquarius' import { getPrice } from '../utils/subgraph' import { MetadataMarket } from '../@types/MetaData' -import { useOcean } from './Ocean' +import { useWeb3 } from './Web3' +import { useSiteMetadata } from '../hooks/useSiteMetadata' interface AssetProviderValue { isInPurgatory: boolean purgatoryData: PurgatoryData - ddo: DDO | undefined - did: string | undefined - metadata: MetadataMarket | undefined - title: string | undefined - owner: string | undefined - price: BestPrice | undefined - type: MetadataMain['type'] | undefined + ddo: DDO + did: string + metadata: MetadataMarket + title: string + owner: string + price: BestPrice + type: MetadataMain['type'] error?: string refreshInterval: number + isAssetNetwork: boolean loading: boolean refreshDdo: (token?: CancelToken) => Promise<void> } @@ -43,7 +45,9 @@ function AssetProvider({ asset: string | DDO children: ReactNode }): ReactElement { - const { config } = useOcean() + const { appConfig } = useSiteMetadata() + + const { networkId } = useWeb3() const [isInPurgatory, setIsInPurgatory] = useState(false) const [purgatoryData, setPurgatoryData] = useState<PurgatoryData>() const [ddo, setDDO] = useState<DDO>() @@ -54,16 +58,13 @@ function AssetProvider({ const [owner, setOwner] = useState<string>() const [error, setError] = useState<string>() const [type, setType] = useState<MetadataMain['type']>() - const [loading, setLoading] = useState<boolean>(false) + const [loading, setLoading] = useState(false) + const [isAssetNetwork, setIsAssetNetwork] = useState<boolean>() const fetchDdo = async (token?: CancelToken) => { Logger.log('[asset] Init asset, get DDO') setLoading(true) - const ddo = await retrieveDDO( - asset as string, - config.metadataCacheUri, - token - ) + const ddo = await retrieveDDO(asset as string, token) if (!ddo) { setError( @@ -88,7 +89,7 @@ function AssetProvider({ // Get and set DDO based on passed DDO or DID // useEffect(() => { - if (!asset || !config?.metadataCacheUri) return + if (!asset || !appConfig.metadataCacheUri) return const source = axios.CancelToken.source() let isMounted = true @@ -105,7 +106,7 @@ function AssetProvider({ isMounted = false source.cancel() } - }, [asset, config?.metadataCacheUri]) + }, [asset, appConfig.metadataCacheUri]) const setPurgatory = useCallback(async (did: string): Promise<void> => { if (!did) return @@ -144,6 +145,14 @@ function AssetProvider({ initMetadata(ddo) }, [ddo, initMetadata]) + // Check user network against asset network + useEffect(() => { + if (!networkId || !ddo) return + + const isAssetNetwork = networkId === ddo?.chainId + setIsAssetNetwork(isAssetNetwork) + }, [networkId, ddo]) + return ( <AssetContext.Provider value={ @@ -160,7 +169,8 @@ function AssetProvider({ purgatoryData, refreshInterval, loading, - refreshDdo + refreshDdo, + isAssetNetwork } as AssetProviderValue } > diff --git a/src/providers/Ocean.tsx b/src/providers/Ocean.tsx index 820b950db..5566e69aa 100644 --- a/src/providers/Ocean.tsx +++ b/src/providers/Ocean.tsx @@ -7,105 +7,74 @@ import React, { ReactNode, useEffect } from 'react' -import { - Ocean, - Logger, - Account, - Config, - ConfigHelperConfig -} from '@oceanprotocol/lib' +import { Ocean, Logger, Account, ConfigHelperConfig } from '@oceanprotocol/lib' import { useWeb3 } from './Web3' -import { - getDevelopmentConfig, - getOceanConfig, - getUserInfo -} from '../utils/ocean' -import { UserBalance } from '../@types/TokenBalance' - -const refreshInterval = 20000 // 20 sec. +import { getDevelopmentConfig, getOceanConfig } from '../utils/ocean' +import { useAsset } from './Asset' interface OceanProviderValue { ocean: Ocean - config: ConfigHelperConfig account: Account - balance: UserBalance - loading: boolean - connect: (config?: Config) => Promise<void> - refreshBalance: () => Promise<void> + config: ConfigHelperConfig + connect: (config: ConfigHelperConfig) => Promise<void> } const OceanContext = createContext({} as OceanProviderValue) -function OceanProvider({ - initialConfig, - children -}: { - initialConfig: Config | ConfigHelperConfig - children: ReactNode -}): ReactElement { - const { web3, accountId, networkId } = useWeb3() +function OceanProvider({ children }: { children: ReactNode }): ReactElement { + const { web3, accountId } = useWeb3() + const { ddo } = useAsset() + const [ocean, setOcean] = useState<Ocean>() const [account, setAccount] = useState<Account>() - const [balance, setBalance] = useState<UserBalance>({ - eth: undefined, - ocean: undefined - }) - const [config, setConfig] = - useState<ConfigHelperConfig | Config>(initialConfig) - const [loading, setLoading] = useState<boolean>() + const [config, setConfig] = useState<ConfigHelperConfig>() // ----------------------------------- - // Create Ocean instance + // Helper: Create Ocean instance // ----------------------------------- const connect = useCallback( - async (newConfig?: ConfigHelperConfig | Config) => { - setLoading(true) + async (config: ConfigHelperConfig) => { + if (!web3) return + + const newConfig: ConfigHelperConfig = { + ...config, + web3Provider: web3 + } + try { - const usedConfig = newConfig || config - Logger.log('[ocean] Connecting Ocean...', usedConfig) - usedConfig.web3Provider = web3 || initialConfig.web3Provider - - if (newConfig) { - await setConfig(usedConfig) - } - - if (usedConfig.web3Provider) { - const newOcean = await Ocean.getInstance(usedConfig) - await setOcean(newOcean) - Logger.log('[ocean] Ocean instance created.', newOcean) - } - setLoading(false) + Logger.log('[ocean] Connecting Ocean...', newConfig) + const newOcean = await Ocean.getInstance(newConfig) + setOcean(newOcean) + setConfig(newConfig) + Logger.log('[ocean] Ocean instance created.', newOcean) } catch (error) { Logger.error('[ocean] Error: ', error.message) } }, - [web3, config, initialConfig.web3Provider] + [web3] ) - async function refreshBalance() { - if (!ocean || !account || !web3) return - - const { balance } = await getUserInfo(ocean) - setBalance(balance) - } - // ----------------------------------- // Initial connection // ----------------------------------- useEffect(() => { + if (!ddo?.chainId) return + + const config = { + ...getOceanConfig(ddo?.chainId), + + // add local dev values + ...(ddo?.chainId === 8996 && { + ...getDevelopmentConfig() + }) + } + async function init() { - await connect() + await connect(config) } init() - - // init periodic refresh of wallet balance - const balanceInterval = setInterval(() => refreshBalance(), refreshInterval) - - return () => { - clearInterval(balanceInterval) - } - }, []) + }, [connect, ddo]) // ----------------------------------- // Get user info, handle account change from web3 @@ -114,51 +83,22 @@ function OceanProvider({ if (!ocean || !accountId || !web3) return async function getInfo() { - const { account, balance } = await getUserInfo(ocean) + const account = (await ocean.accounts.list())[0] + Logger.log('[ocean] Account: ', account) setAccount(account) - setBalance(balance) } getInfo() }, [ocean, accountId, web3]) - // ----------------------------------- - // Handle network change from web3 - // ----------------------------------- - useEffect(() => { - if (!networkId) return - - async function reconnect() { - const newConfig = { - ...getOceanConfig(networkId), - - // add local dev values - ...(networkId === 8996 && { - ...getDevelopmentConfig() - }) - } - - try { - setLoading(true) - await connect(newConfig) - setLoading(false) - } catch (error) { - Logger.error('[ocean] Error: ', error.message) - } - } - reconnect() - }, [networkId]) - return ( <OceanContext.Provider value={ { ocean, account, - balance, - config, - loading, connect, - refreshBalance + config + // refreshBalance } as OceanProviderValue } > diff --git a/src/providers/UrqlProvider.tsx b/src/providers/UrqlProvider.tsx new file mode 100644 index 000000000..40129be4d --- /dev/null +++ b/src/providers/UrqlProvider.tsx @@ -0,0 +1,46 @@ +import { createClient, Provider, Client } from 'urql' +import React, { useState, useEffect, ReactNode, ReactElement } from 'react' +import { useWeb3 } from './Web3' +import { Logger } from '@oceanprotocol/lib' +import { getOceanConfig } from '../utils/ocean' + +let urqlClient: Client + +function createUrqlClient(subgraphUri: string) { + const client = createClient({ + url: `${subgraphUri}/subgraphs/name/oceanprotocol/ocean-subgraph` + }) + return client +} + +export function getUrqlClientInstance(): Client { + return urqlClient +} + +export default function UrqlClientProvider({ + children +}: { + children: ReactNode +}): ReactElement { + const { networkId } = useWeb3() + const [client, setClient] = useState<Client>() + + useEffect(() => { + const oceanConfig = getOceanConfig(networkId || 1) + + if (!oceanConfig?.subgraphUri) { + Logger.error( + 'No subgraphUri defined, preventing UrqlProvider from initialization.' + ) + return + } + + const newClient = createUrqlClient(oceanConfig.subgraphUri) + urqlClient = newClient + setClient(newClient) + Logger.log(`[URQL] Client connected to ${oceanConfig.subgraphUri}`) + }, [networkId]) + + return client ? <Provider value={client}>{children}</Provider> : <></> +} +export { UrqlClientProvider } diff --git a/src/providers/UserPreferences.tsx b/src/providers/UserPreferences.tsx index 04ad39296..b83e31ed2 100644 --- a/src/providers/UserPreferences.tsx +++ b/src/providers/UserPreferences.tsx @@ -6,17 +6,17 @@ import React, { useState, useEffect } from 'react' -import { Logger, LogLevel, ConfigHelperConfig } from '@oceanprotocol/lib' -import { useOcean } from './Ocean' +import { Logger, LogLevel } from '@oceanprotocol/lib' import { isBrowser } from '../utils' +import { useSiteMetadata } from '../hooks/useSiteMetadata' interface UserPreferencesValue { debug: boolean currency: string locale: string - bookmarks: { - [network: string]: string[] - } + chainIds: number[] + bookmarks: string[] + setChainIds: (chainIds: number[]) => void setDebug: (value: boolean) => void setCurrency: (value: string) => void addBookmark: (did: string) => void @@ -45,8 +45,7 @@ function UserPreferencesProvider({ }: { children: ReactNode }): ReactElement { - const { config } = useOcean() - const networkName = (config as ConfigHelperConfig).network + const { appConfig } = useSiteMetadata() const localStorage = getLocalStorage() // Set default values from localStorage @@ -55,12 +54,15 @@ function UserPreferencesProvider({ localStorage?.currency || 'EUR' ) const [locale, setLocale] = useState<string>() - const [bookmarks, setBookmarks] = useState(localStorage?.bookmarks || {}) + const [bookmarks, setBookmarks] = useState(localStorage?.bookmarks || []) + const [chainIds, setChainIds] = useState( + localStorage?.chainIds || appConfig.chainIds + ) // Write values to localStorage on change useEffect(() => { - setLocalStorage({ debug, currency, bookmarks }) - }, [debug, currency, bookmarks]) + setLocalStorage({ chainIds, debug, currency, bookmarks }) + }, [chainIds, debug, currency, bookmarks]) // Set ocean.js log levels, default: Error useEffect(() => { @@ -76,27 +78,24 @@ function UserPreferencesProvider({ }, []) function addBookmark(didToAdd: string): void { - const newPinned = { - ...bookmarks, - [networkName]: [didToAdd].concat(bookmarks[networkName]) - } + const newPinned = [...bookmarks, didToAdd] setBookmarks(newPinned) } function removeBookmark(didToAdd: string): void { - const newPinned = { - ...bookmarks, - [networkName]: bookmarks[networkName].filter( - (did: string) => did !== didToAdd - ) - } + const newPinned = bookmarks.filter((did: string) => did !== didToAdd) setBookmarks(newPinned) } // Bookmarks old data structure migration useEffect(() => { - if (!bookmarks.length) return - const newPinned = { mainnet: bookmarks as any } + if (bookmarks.length !== undefined) return + const newPinned: string[] = [] + for (const network in bookmarks) { + ;(bookmarks[network] as unknown as string[]).forEach((did: string) => { + did !== null && newPinned.push(did) + }) + } setBookmarks(newPinned) }, [bookmarks]) @@ -107,7 +106,9 @@ function UserPreferencesProvider({ debug, currency, locale, + chainIds, bookmarks, + setChainIds, setDebug, setCurrency, addBookmark, diff --git a/src/providers/Web3.tsx b/src/providers/Web3.tsx index 8db6f08e3..09eca99eb 100644 --- a/src/providers/Web3.tsx +++ b/src/providers/Web3.tsx @@ -15,10 +15,13 @@ import { Logger } from '@oceanprotocol/lib' import { isBrowser } from '../utils' import { EthereumListsChain, - getNetworkData, + getNetworkDataById, getNetworkDisplayName } from '../utils/web3' -import { graphql, useStaticQuery } from 'gatsby' +import { graphql } from 'gatsby' +import { UserBalance } from '../@types/TokenBalance' +import { getOceanBalance } from '../utils/ocean' +import useNetworkMetadata from '../hooks/useNetworkMetadata' interface Web3ProviderValue { web3: Web3 @@ -26,6 +29,7 @@ interface Web3ProviderValue { web3Modal: Web3Modal web3ProviderInfo: IProviderInfo accountId: string + balance: UserBalance networkId: number chainId: number networkDisplayName: string @@ -78,6 +82,8 @@ export const web3ModalOpts = { theme: web3ModalTheme } +const refreshInterval = 20000 // 20 sec. + const networksQuery = graphql` query { allNetworksMetadataJson { @@ -101,9 +107,7 @@ const networksQuery = graphql` const Web3Context = createContext({} as Web3ProviderValue) function Web3Provider({ children }: { children: ReactNode }): ReactElement { - const data = useStaticQuery(networksQuery) - const networksList: { node: EthereumListsChain }[] = - data.allNetworksMetadataJson.edges + const { networksList } = useNetworkMetadata() const [web3, setWeb3] = useState<Web3>() const [web3Provider, setWeb3Provider] = useState<any>() @@ -117,7 +121,14 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement { const [isTestnet, setIsTestnet] = useState<boolean>() const [accountId, setAccountId] = useState<string>() const [web3Loading, setWeb3Loading] = useState<boolean>(true) + const [balance, setBalance] = useState<UserBalance>({ + eth: '0', + ocean: '0' + }) + // ----------------------------------- + // Helper: connect to web3 + // ----------------------------------- const connect = useCallback(async () => { if (!web3Modal) { setWeb3Loading(false) @@ -152,6 +163,24 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement { } }, [web3Modal]) + // ----------------------------------- + // Helper: Get user balance + // ----------------------------------- + const getUserBalance = useCallback(async () => { + if (!accountId || !networkId || !web3) return + + try { + const balance = { + eth: web3.utils.fromWei(await web3.eth.getBalance(accountId, 'latest')), + ocean: await getOceanBalance(accountId, networkId, web3) + } + setBalance(balance) + Logger.log('[web3] Balance: ', balance) + } catch (error) { + Logger.error('[web3] Error: ', error.message) + } + }, [accountId, networkId, web3]) + // ----------------------------------- // Create initial Web3Modal instance // ----------------------------------- @@ -186,13 +215,26 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement { connectCached() }, [connect, web3Modal]) + // ----------------------------------- + // Get and set user balance + // ----------------------------------- + useEffect(() => { + getUserBalance() + + // init periodic refresh of wallet balance + const balanceInterval = setInterval(() => getUserBalance(), refreshInterval) + + return () => { + clearInterval(balanceInterval) + } + }, [getUserBalance]) + // ----------------------------------- // Get and set network metadata // ----------------------------------- useEffect(() => { if (!networkId) return - - const networkData = getNetworkData(networksList, networkId) + const networkData = getNetworkDataById(networksList, networkId) setNetworkData(networkData) Logger.log( networkData @@ -291,6 +333,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement { web3Modal, web3ProviderInfo, accountId, + balance, networkId, chainId, networkDisplayName, diff --git a/src/utils/aquarius.ts b/src/utils/aquarius.ts index e00f8faac..10adee35c 100644 --- a/src/utils/aquarius.ts +++ b/src/utils/aquarius.ts @@ -12,12 +12,13 @@ import { import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection' import { PriceList, getAssetsPriceList } from './subgraph' import axios, { CancelToken, AxiosResponse } from 'axios' +import { metadataCacheUri } from '../../app.config' -function getQueryForAlgorithmDatasets(algorithmDid: string) { +function getQueryForAlgorithmDatasets(algorithmDid: string, chainId?: number) { return { query: { query_string: { - query: `service.attributes.main.privacy.publisherTrustedAlgorithms.did:${algorithmDid}` + query: `service.attributes.main.privacy.publisherTrustedAlgorithms.did:${algorithmDid} AND chainId:${chainId}` } }, sort: { created: -1 } @@ -49,18 +50,25 @@ export function transformQueryResult( } } +export function transformChainIdsListToQuery(chainIds: number[]): string { + let chainQuery = '' + chainIds.forEach((chainId) => { + chainQuery += `chainId:${chainId} OR ` + }) + chainQuery = chainQuery.slice(0, chainQuery.length - 4) + return chainQuery +} + export async function queryMetadata( query: SearchQuery, - metadataCacheUri: string, cancelToken: CancelToken ): Promise<QueryResult> { try { - const response: AxiosResponse<QueryResult> = await axios.post( + const response: AxiosResponse<any> = await axios.post( `${metadataCacheUri}/api/v1/aquarius/assets/ddo/query`, { ...query, cancelToken } ) if (!response || response.status !== 200 || !response.data) return - return transformQueryResult(response.data) } catch (error) { if (axios.isCancel(error)) { @@ -73,7 +81,6 @@ export async function queryMetadata( export async function retrieveDDO( did: string | DID, - metadataCacheUri: string, cancelToken: CancelToken ): Promise<DDO> { try { @@ -83,7 +90,8 @@ export async function retrieveDDO( ) if (!response || response.status !== 200 || !response.data) return - return new DDO(response.data) + const data = { ...response.data } + return new DDO(data) } catch (error) { if (axios.isCancel(error)) { Logger.log(error.message) @@ -95,7 +103,6 @@ export async function retrieveDDO( export async function getAssetsNames( didList: string[] | DID[], - metadataCacheUri: string, cancelToken: CancelToken ): Promise<Record<string, string>> { try { @@ -120,7 +127,6 @@ export async function getAssetsNames( export async function transformDDOToAssetSelection( datasetProviderEndpoint: string, ddoList: DDO[], - metadataCacheUri: string, selectedAlgorithms?: PublisherTrustedAlgorithm[] ): Promise<AssetSelectionAsset[]> { const source = axios.CancelToken.source() @@ -135,7 +141,7 @@ export async function transformDDOToAssetSelection( algoComputeService?.serviceEndpoint && (didProviderEndpointMap[ddo.id] = algoComputeService?.serviceEndpoint) } - const ddoNames = await getAssetsNames(didList, metadataCacheUri, source.token) + const ddoNames = await getAssetsNames(didList, source.token) const algorithmList: AssetSelectionAsset[] = [] didList?.forEach((did: string) => { if ( @@ -172,12 +178,11 @@ export async function transformDDOToAssetSelection( export async function getAlgorithmDatasetsForCompute( algorithmId: string, datasetProviderUri: string, - metadataCacheUri: string + datasetChainId?: number ): Promise<AssetSelectionAsset[]> { const source = axios.CancelToken.source() const computeDatasets = await queryMetadata( - getQueryForAlgorithmDatasets(algorithmId), - metadataCacheUri, + getQueryForAlgorithmDatasets(algorithmId, datasetChainId), source.token ) const computeDatasetsForCurrentAlgorithm: DDO[] = [] @@ -195,7 +200,6 @@ export async function getAlgorithmDatasetsForCompute( const datasets = await transformDDOToAssetSelection( datasetProviderUri, computeDatasetsForCurrentAlgorithm, - metadataCacheUri, [] ) return datasets diff --git a/src/utils/index.ts b/src/utils/index.ts index ca935dd2e..285a24271 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -59,3 +59,11 @@ export function sleep(ms: number): Promise<void> { setTimeout(resolve, ms) }) } + +export function removeItemFromArray<T>(arr: Array<T>, value: T): Array<T> { + const index = arr.indexOf(value) + if (index > -1) { + arr.splice(index, 1) + } + return arr +} diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts index 691576286..682c674d6 100644 --- a/src/utils/metadata.ts +++ b/src/utils/metadata.ts @@ -239,3 +239,29 @@ export function transformPublishAlgorithmFormToMetadata( return metadata } + +function idToName(id: number): string { + switch (id) { + case 1: + return 'eth' + case 137: + return 'polygon' + case 3: + return 'ropsten' + case 4: + return 'rinkeby' + case 1287: + return 'moonbase' + default: + return 'eth' + } +} + +export function mapChainIdsToNetworkNames(chainIds: number[]): string[] { + const networkNames: string[] = [] + for (let i = 0; i < chainIds.length; i++) { + const networkName = idToName(chainIds[i]) + networkNames.push(networkName) + } + return networkNames +} diff --git a/src/utils/ocean.ts b/src/utils/ocean.ts index 76735a3cb..5a4245045 100644 --- a/src/utils/ocean.ts +++ b/src/utils/ocean.ts @@ -1,18 +1,13 @@ -import { - Account, - Logger, - Ocean, - ConfigHelper, - ConfigHelperConfig -} from '@oceanprotocol/lib' +import { ConfigHelper, ConfigHelperConfig, Logger } from '@oceanprotocol/lib' import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json' - -import { UserBalance } from '../@types/TokenBalance' +import { AbiItem } from 'web3-utils/types' +import Web3 from 'web3' export function getOceanConfig(network: string | number): ConfigHelperConfig { - return new ConfigHelper().getConfig( + const config = new ConfigHelper().getConfig( network, - network === 'moonbeamalpha' || + network === 'polygon' || + network === 'moonbeamalpha' || network === 1287 || network === 'bsc' || network === 56 || @@ -20,7 +15,9 @@ export function getOceanConfig(network: string | number): ConfigHelperConfig { network === 2021000 ? undefined : process.env.GATSBY_INFURA_PROJECT_ID - ) as ConfigHelperConfig + ) + + return config as ConfigHelperConfig } export function getDevelopmentConfig(): Partial<ConfigHelperConfig> { @@ -35,19 +32,44 @@ export function getDevelopmentConfig(): Partial<ConfigHelperConfig> { } } -export async function getUserInfo( - ocean: Ocean -): Promise<{ account: Account; balance: UserBalance }> { - if (!ocean) return { account: null, balance: { eth: '0', ocean: '0' } } +export async function getOceanBalance( + accountId: string, + networkId: number, + web3: Web3 +): Promise<string> { + const minABI = [ + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address' + } + ], + name: 'balanceOf', + outputs: [ + { + name: 'balance', + type: 'uint256' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + } + ] as AbiItem[] - const account = (await ocean.accounts.list())[0] - Logger.log('[ocean] Account: ', account) - - const balance = { - eth: await account.getEtherBalance(), - ocean: await account.getOceanBalance() + try { + const token = new web3.eth.Contract( + minABI, + getOceanConfig(networkId).oceanTokenAddress, + { from: accountId } + ) + const result = web3.utils.fromWei( + await token.methods.balanceOf(accountId).call() + ) + return result + } catch (e) { + Logger.error(`ERROR: Failed to get the balance: ${e.message}`) } - Logger.log('[ocean] Balance: ', JSON.stringify(balance)) - - return { account, balance } } diff --git a/src/utils/subgraph.ts b/src/utils/subgraph.ts index 115f8c432..5279988a0 100644 --- a/src/utils/subgraph.ts +++ b/src/utils/subgraph.ts @@ -1,6 +1,8 @@ -import { gql, DocumentNode, ApolloQueryResult } from '@apollo/client' +import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql' import { DDO, BestPrice } from '@oceanprotocol/lib' -import { getApolloClientInstance } from '../providers/ApolloClientProvider' +import { getUrqlClientInstance } from '../providers/UrqlProvider' +import { getOceanConfig } from './ocean' +import web3 from 'web3' import { AssetsPoolPrice, AssetsPoolPrice_pools as AssetsPoolPricePools @@ -9,12 +11,15 @@ import { AssetsFrePrice, AssetsFrePrice_fixedRateExchanges as AssetsFrePriceFixedRateExchanges } from '../@types/apollo/AssetsFrePrice' -import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder' import { AssetsFreePrice, AssetsFreePrice_dispensers as AssetFreePriceDispenser } from '../@types/apollo/AssetsFreePrice' -import web3 from 'web3' +import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder' +import { + HighestLiquidityAssets_pools as HighestLiquidityAssetsPools, + HighestLiquidityAssets as HighestLiquidityGraphAssets +} from '../@types/apollo/HighestLiquidityAssets' export interface PriceList { [key: string]: string @@ -121,37 +126,75 @@ const PreviousOrderQuery = gql` } ` const HighestLiquidityAssets = gql` - query HighestLiquidiyAssets { + query HighestLiquidityAssets { pools( where: { datatokenReserve_gte: 1 } - orderBy: valueLocked + orderBy: oceanReserve orderDirection: desc first: 15 ) { id datatokenAddress valueLocked + oceanReserve } } ` -async function fetchData( - query: DocumentNode, - variables: any -): Promise<ApolloQueryResult<any>> { +export function getSubgrahUri(chainId: number): string { + const config = getOceanConfig(chainId) + return config.subgraphUri +} + +export function getQueryContext(chainId: number): OperationContext { + const queryContext: OperationContext = { + url: `${getSubgrahUri( + Number(chainId) + )}/subgraphs/name/oceanprotocol/ocean-subgraph`, + requestPolicy: 'network-only' + } + + return queryContext +} + +export async function fetchData( + query: TypedDocumentNode, + variables: any, + context: OperationContext +): Promise<any> { try { - const client = getApolloClientInstance() - const response = await client.query({ - query: query, - variables: variables, - fetchPolicy: 'no-cache' - }) + const client = getUrqlClientInstance() + const response = await client.query(query, variables, context).toPromise() return response } catch (error) { console.error('Error fetchData: ', error.message) + throw Error(error.message) } } +export async function fetchDataForMultipleChains( + query: TypedDocumentNode, + variables: any, + chainIds: number[] +): Promise<any[]> { + let datas: any[] = [] + for (const chainId of chainIds) { + const context: OperationContext = { + url: `${getSubgrahUri( + chainId + )}/subgraphs/name/oceanprotocol/ocean-subgraph`, + requestPolicy: 'network-only' + } + try { + const response = await fetchData(query, variables, context) + datas = datas.concat(response.data) + } catch (error) { + console.error('Error fetchData: ', error.message) + } + } + return datas +} + export async function getPreviousOrders( id: string, account: string, @@ -161,8 +204,8 @@ export async function getPreviousOrders( id: id, account: account } - const fetchedPreviousOrders: ApolloQueryResult<AssetPreviousOrder> = - await fetchData(PreviousOrderQuery, variables) + const fetchedPreviousOrders: OperationResult<AssetPreviousOrder> = + await fetchData(PreviousOrderQuery, variables, null) if (fetchedPreviousOrders.data?.tokenOrders?.length === 0) return null if (assetTimeout === '0') { return fetchedPreviousOrders?.data?.tokenOrders[0]?.tx @@ -242,44 +285,62 @@ async function getAssetsPoolsExchangesAndDatatokenMap( assets: DDO[] ): Promise< [ - ApolloQueryResult<AssetsPoolPrice>, - ApolloQueryResult<AssetsFrePrice>, - ApolloQueryResult<AssetsFreePrice>, + AssetsPoolPricePools[], + AssetsFrePriceFixedRateExchanges[], + AssetFreePriceDispenser[], DidAndDatatokenMap ] > { const didDTMap: DidAndDatatokenMap = {} - const dataTokenList: string[] = [] + const chainAssetLists: any = {} for (const ddo of assets) { didDTMap[ddo?.dataToken.toLowerCase()] = ddo.id - dataTokenList.push(ddo?.dataToken.toLowerCase()) + // harcoded until we have chainId on assets + if (chainAssetLists[ddo.chainId]) { + chainAssetLists[ddo.chainId].push(ddo?.dataToken.toLowerCase()) + } else { + chainAssetLists[ddo.chainId] = [] + chainAssetLists[ddo.chainId].push(ddo?.dataToken.toLowerCase()) + } } - const freVariables = { - datatoken_in: dataTokenList + let poolPriceResponse: AssetsPoolPricePools[] = [] + let frePriceResponse: AssetsFrePriceFixedRateExchanges[] = [] + let freePriceResponse: AssetFreePriceDispenser[] = [] + + for (const chainKey in chainAssetLists) { + const freVariables = { + datatoken_in: chainAssetLists[chainKey] + } + const poolVariables = { + datatokenAddress_in: chainAssetLists[chainKey] + } + const freeVariables = { + datatoken_in: chainAssetLists[chainKey] + } + + const queryContext = getQueryContext(Number(chainKey)) + + const chainPoolPriceResponse: OperationResult<AssetsPoolPrice> = + await fetchData(PoolQuery, poolVariables, queryContext) + + poolPriceResponse = poolPriceResponse.concat( + chainPoolPriceResponse.data.pools + ) + const chainFrePriceResponse: OperationResult<AssetsFrePrice> = + await fetchData(FreQuery, freVariables, queryContext) + + frePriceResponse = frePriceResponse.concat( + chainFrePriceResponse.data.fixedRateExchanges + ) + + const chainFreePriceResponse: OperationResult<AssetsFreePrice> = + await fetchData(FreeQuery, freeVariables, queryContext) + + freePriceResponse = freePriceResponse.concat( + chainFreePriceResponse.data.dispensers + ) } - const poolVariables = { - datatokenAddress_in: dataTokenList - } - - const freeVariables = { - datatoken_in: dataTokenList - } - - const poolPriceResponse: ApolloQueryResult<AssetsPoolPrice> = await fetchData( - PoolQuery, - poolVariables - ) - const frePriceResponse: ApolloQueryResult<AssetsFrePrice> = await fetchData( - FreQuery, - freVariables - ) - - const freePriceResponse: ApolloQueryResult<AssetsFreePrice> = await fetchData( - FreeQuery, - freeVariables - ) - return [poolPriceResponse, frePriceResponse, freePriceResponse, didDTMap] } @@ -287,9 +348,9 @@ export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> { const priceList: PriceList = {} const values: [ - ApolloQueryResult<AssetsPoolPrice>, - ApolloQueryResult<AssetsFrePrice>, - ApolloQueryResult<AssetsFreePrice>, + AssetsPoolPricePools[], + AssetsFrePriceFixedRateExchanges[], + AssetFreePriceDispenser[], DidAndDatatokenMap ] = await getAssetsPoolsExchangesAndDatatokenMap(assets) const poolPriceResponse = values[0] @@ -297,16 +358,16 @@ export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> { const freePriceResponse = values[2] const didDTMap: DidAndDatatokenMap = values[3] - for (const poolPrice of poolPriceResponse.data?.pools) { + for (const poolPrice of poolPriceResponse) { priceList[didDTMap[poolPrice.datatokenAddress]] = poolPrice.consumePrice === '-1' ? poolPrice.spotPrice : poolPrice.consumePrice } - for (const frePrice of frePriceResponse.data?.fixedRateExchanges) { + for (const frePrice of frePriceResponse) { priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.rate } - for (const freePrice of freePriceResponse.data?.dispensers) { + for (const freePrice of freePriceResponse) { priceList[didDTMap[freePrice.datatoken?.address]] = '0' } return priceList @@ -316,26 +377,28 @@ export async function getPrice(asset: DDO): Promise<BestPrice> { const freVariables = { datatoken: asset?.dataToken.toLowerCase() } - - const poolVariables = { - datatokenAddress: asset?.dataToken.toLowerCase() - } - const freeVariables = { datatoken: asset?.dataToken.toLowerCase() } + const poolVariables = { + datatokenAddress: asset?.dataToken.toLowerCase() + } + const queryContext = getQueryContext(Number(asset.chainId)) - const poolPriceResponse: ApolloQueryResult<AssetsPoolPrice> = await fetchData( + const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData( AssetPoolPriceQuerry, - poolVariables + poolVariables, + queryContext ) - const frePriceResponse: ApolloQueryResult<AssetsFrePrice> = await fetchData( + const frePriceResponse: OperationResult<AssetsFrePrice> = await fetchData( AssetFreQuery, - freVariables + freVariables, + queryContext ) - const freePriceResponse: ApolloQueryResult<AssetsFreePrice> = await fetchData( + const freePriceResponse: OperationResult<AssetsFreePrice> = await fetchData( AssetFreeQuery, - freeVariables + freeVariables, + queryContext ) const bestPrice: BestPrice = transformPriceToBestPrice( @@ -353,29 +416,29 @@ export async function getAssetsBestPrices( const assetsWithPrice: AssetListPrices[] = [] const values: [ - ApolloQueryResult<AssetsPoolPrice>, - ApolloQueryResult<AssetsFrePrice>, - ApolloQueryResult<AssetsFreePrice>, + AssetsPoolPricePools[], + AssetsFrePriceFixedRateExchanges[], + AssetFreePriceDispenser[], DidAndDatatokenMap ] = await getAssetsPoolsExchangesAndDatatokenMap(assets) + const poolPriceResponse = values[0] const frePriceResponse = values[1] const freePriceResponse = values[2] - for (const ddo of assets) { const dataToken = ddo.dataToken.toLowerCase() const poolPrice: AssetsPoolPricePools[] = [] const frePrice: AssetsFrePriceFixedRateExchanges[] = [] const freePrice: AssetFreePriceDispenser[] = [] - const pool = poolPriceResponse.data?.pools.find( + const pool = poolPriceResponse.find( (pool: any) => pool.datatokenAddress === dataToken ) pool && poolPrice.push(pool) - const fre = frePriceResponse.data?.fixedRateExchanges.find( + const fre = frePriceResponse.find( (fre: any) => fre.datatoken.address === dataToken ) fre && frePrice.push(fre) - const free = freePriceResponse.data?.dispensers.find( + const free = freePriceResponse.find( (free: any) => free.datatoken.address === dataToken ) free && freePrice.push(free) @@ -389,14 +452,26 @@ export async function getAssetsBestPrices( return assetsWithPrice } -export async function getHighestLiquidityDIDs(): Promise<string> { +export async function getHighestLiquidityDIDs( + chainIds: number[] +): Promise<string> { const didList: string[] = [] - const fetchedPools = await fetchData(HighestLiquidityAssets, null) - if (fetchedPools.data?.pools?.length === 0) return null - for (let i = 0; i < fetchedPools.data.pools.length; i++) { - if (!fetchedPools.data.pools[i].datatokenAddress) continue + let highestLiquidiyAssets: HighestLiquidityAssetsPools[] = [] + for (const chain of chainIds) { + const queryContext = getQueryContext(Number(chain)) + const fetchedPools: OperationResult<HighestLiquidityGraphAssets, any> = + await fetchData(HighestLiquidityAssets, null, queryContext) + highestLiquidiyAssets = highestLiquidiyAssets.concat( + fetchedPools.data.pools + ) + } + highestLiquidiyAssets + .sort((a, b) => a.oceanReserve - b.oceanReserve) + .reverse() + for (let i = 0; i < highestLiquidiyAssets.length; i++) { + if (!highestLiquidiyAssets[i].datatokenAddress) continue const did = web3.utils - .toChecksumAddress(fetchedPools.data.pools[i].datatokenAddress) + .toChecksumAddress(highestLiquidiyAssets[i].datatokenAddress) .replace('0x', 'did:op:') didList.push(did) } diff --git a/src/utils/web3.ts b/src/utils/web3.ts index a3bc91ce9..ac068c0c9 100644 --- a/src/utils/web3.ts +++ b/src/utils/web3.ts @@ -19,6 +19,16 @@ export interface NetworkObject { urlList: string[] } +export function getNetworkConfigObject(node: any): NetworkObject { + const networkConfig = { + name: node.chain, + symbol: node.nativeCurrency.symbol, + chainId: node.chainId, + urlList: [node.providerUri] + } + return networkConfig +} + export function accountTruncate(account: string): string { if (!account) return const middle = account.substring(6, 38) @@ -30,21 +40,39 @@ export function getNetworkDisplayName( data: EthereumListsChain, networkId: number ): string { - const displayName = data - ? `${data.chain} ${data.network === 'mainnet' ? '' : data.network}` - : networkId === 2021000 - ? 'GAIA-X' - : networkId === 8996 - ? 'Development' - : 'Unknown' + let displayName + + switch (networkId) { + case 1287: + displayName = 'Moonbase Alpha' + break + case 137: + displayName = 'Polygon' + break + case 80001: + displayName = 'Polygon Mumbai' + break + case 8996: + displayName = 'Development' + break + case 2021000: + displayName = 'GAIA-X' + break + default: + displayName = data + ? `${data.chain} ${data.network === 'mainnet' ? '' : data.network}` + : 'Unknown' + break + } return displayName } -export function getNetworkData( +export function getNetworkDataById( data: { node: EthereumListsChain }[], networkId: number ): EthereumListsChain { + if (!networkId) return const networkData = data.filter( ({ node }: { node: EthereumListsChain }) => node.chainId === networkId ) @@ -52,34 +80,48 @@ export function getNetworkData( return networkData[0]?.node } -export function addCustomNetwork( +export async function addCustomNetwork( web3Provider: any, network: NetworkObject -): void { +): Promise<void> { const newNewtworkData = { chainId: `0x${network.chainId.toString(16)}`, chainName: network.name, rpcUrls: network.urlList } - web3Provider.request( - { - method: 'wallet_addEthereumChain', - params: [newNewtworkData] - }, - (err: string, added: any) => { - if (err || 'error' in added) { - Logger.error( - `Couldn't add ${network.name} (0x${ - network.chainId - }) netowrk to MetaMask, error: ${err || added.error}` - ) - } else { - Logger.log( - `Added ${network.name} (0x${network.chainId}) network to MetaMask` - ) - } + try { + await web3Provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: newNewtworkData.chainId }] + }) + } catch (switchError) { + if (switchError.code === 4902) { + web3Provider.request( + { + method: 'wallet_addEthereumChain', + params: [newNewtworkData] + }, + (err: string, added: any) => { + if (err || 'error' in added) { + Logger.error( + `Couldn't add ${network.name} (0x${ + network.chainId + }) netowrk to MetaMask, error: ${err || added.error}` + ) + } else { + Logger.log( + `Added ${network.name} (0x${network.chainId}) network to MetaMask` + ) + } + } + ) + } else { + Logger.error( + `Couldn't add ${network.name} (0x${network.chainId}) netowrk to MetaMask, error: ${switchError}` + ) } - ) + } + Logger.log(`Added ${network.name} (0x${network.chainId}) network to MetaMask`) } export async function addTokenToWallet(