mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge pull request #628 from oceanprotocol/feature/multinetwork
Multinetwork Interface
This commit is contained in:
commit
4c653b6ae8
@ -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_INFURA_PROJECT_ID="xxx"
|
||||||
#GATSBY_MARKET_FEE_ADDRESS="0xxx"
|
#GATSBY_MARKET_FEE_ADDRESS="0xxx"
|
||||||
|
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@ -16,7 +16,6 @@ jobs:
|
|||||||
|
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
env:
|
env:
|
||||||
GATSBY_NETWORK: ${{ secrets.GATSBY_NETWORK }}
|
|
||||||
GATSBY_INFURA_PROJECT_ID: ${{ secrets.GATSBY_INFURA_PROJECT_ID }}
|
GATSBY_INFURA_PROJECT_ID: ${{ secrets.GATSBY_INFURA_PROJECT_ID }}
|
||||||
GATSBY_PORTIS_ID: ${{ secrets.GATSBY_PORTIS_ID }}
|
GATSBY_PORTIS_ID: ${{ secrets.GATSBY_PORTIS_ID }}
|
||||||
|
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -13,4 +13,6 @@ public/storybook
|
|||||||
.vercel
|
.vercel
|
||||||
repo-metadata.json
|
repo-metadata.json
|
||||||
content/networks-metadata.json
|
content/networks-metadata.json
|
||||||
src/@types/apollo
|
src/@types/apollo
|
||||||
|
graphql.schema.json
|
||||||
|
src/@types/graph.types.ts
|
||||||
|
31
README.md
31
README.md
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
## 🏄 Get Started
|
## 🏄 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:
|
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:
|
Finally, set environment variables to use this local connection in `.env` in the app:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# modify env variables, setting GATSBY_NETWORK="development"
|
# modify env variables
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
|
||||||
npm start
|
npm start
|
||||||
@ -111,7 +111,7 @@ All this data then comes from multiple sources:
|
|||||||
|
|
||||||
### Aquarius
|
### 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)
|
- 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
|
- hit Aquarius as early as possible without relying on any ocean.js initialization
|
||||||
@ -127,25 +127,22 @@ const queryLatest = {
|
|||||||
offset: 9,
|
offset: 9,
|
||||||
query: {
|
query: {
|
||||||
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
|
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
|
||||||
|
|
||||||
query_string: { query: `-isInPurgatory:true` }
|
query_string: { query: `-isInPurgatory:true` }
|
||||||
},
|
},
|
||||||
sort: { created: -1 }
|
sort: { created: -1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
function Component() {
|
function Component() {
|
||||||
const { config } = useOcean()
|
const { appConfig } = useSiteMetadata()
|
||||||
const [result, setResult] = useState<QueryResult>()
|
const [result, setResult] = useState<QueryResult>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!config?.metadataCacheUri) return
|
if (!appConfig.metadataCacheUri) return
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const result = await queryMetadata(
|
const result = await queryMetadata(query, source.token)
|
||||||
query,
|
|
||||||
config.metadataCacheUri,
|
|
||||||
source.token
|
|
||||||
)
|
|
||||||
setResult(result)
|
setResult(result)
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
@ -153,7 +150,7 @@ function Component() {
|
|||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [config?.metadataCacheUri, query])
|
}, [appConfig.metadataCacheUri, query])
|
||||||
|
|
||||||
return <div>{result}</div>
|
return <div>{result}</div>
|
||||||
}
|
}
|
||||||
@ -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.
|
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
|
```tsx
|
||||||
import { gql, useQuery } from '@apollo/client'
|
import { gql, useQuery } from 'urql'
|
||||||
|
|
||||||
const query = gql`
|
const query = gql`
|
||||||
query PoolLiquidity($id: ID!, $shareId: ID) {
|
query PoolLiquidity($id: ID!, $shareId: ID) {
|
||||||
@ -266,10 +263,14 @@ Within components this metadata can be queried for under `allNetworksMetadataJso
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
export default function NetworkName(): ReactElement {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{networkDisplayName} {isTestnet && `(Test)`}
|
{networkName} {isTestnet && `(Test)`}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
// The default network and its associated config the app should connect to
|
// URI of single metadata cache instance for all networks.
|
||||||
// on start. App will automatically switch network configs when user switches
|
// While ocean.js includes this value for each network as part of its ConfigHelper,
|
||||||
// networks in their wallet.
|
// it is assumed to be the same for all networks.
|
||||||
// Ocean Protocol contracts are deployed for: 'mainnet', 'rinkeby', 'development'
|
// In components can be accessed with the useSiteMetadata hook:
|
||||||
network: process.env.GATSBY_NETWORK || 'mainnet',
|
// 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,
|
rbacUrl: process.env.GATSBY_RBAC_URL,
|
||||||
|
|
||||||
infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx',
|
infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx',
|
||||||
|
22
codegen.yml
Normal file
22
codegen.yml
Normal file
@ -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'
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Publish",
|
"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.",
|
"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."
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ execSync(`node ./scripts/write-repo-metadata > repo-metadata.json`, {
|
|||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Generate GraphQl typings for urql
|
||||||
|
// execSync(`npm run graphql:graphTypes`, { stdio: 'inherit' })
|
||||||
|
|
||||||
// Generate Apollo typings
|
// Generate Apollo typings
|
||||||
execSync(`npm run apollo:codegen`, { stdio: 'inherit' })
|
execSync(`npm run apollo:codegen`, { stdio: 'inherit' })
|
||||||
|
|
||||||
|
2398
package-lock.json
generated
2398
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -9,6 +9,7 @@
|
|||||||
"build": "gatsby build && cp _redirects public/_redirects",
|
"build": "gatsby build && cp _redirects public/_redirects",
|
||||||
"serve": "serve -s public/",
|
"serve": "serve -s public/",
|
||||||
"jest": "NODE_ENV=test jest -c tests/unit/jest.config.js",
|
"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": "npm run apollo:codegen && npm run lint && npm run jest",
|
||||||
"test:watch": "npm run lint && npm run jest -- --watch",
|
"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",
|
"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",
|
"storybook:build": "build-storybook -c .storybook -o public/storybook",
|
||||||
"write:repoMetadata": "node ./scripts/write-repo-metadata > repo-metadata.json",
|
"write:repoMetadata": "node ./scripts/write-repo-metadata > repo-metadata.json",
|
||||||
"deploy:s3": "./scripts/deploy-s3.sh",
|
"deploy:s3": "./scripts/deploy-s3.sh",
|
||||||
|
"postinstall": "husky install",
|
||||||
"apollo:codegen": "apollo client:codegen --target typescript --tsFileExtension=d.ts --outputFlat src/@types/apollo/",
|
"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": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.3.19",
|
|
||||||
"@coingecko/cryptoformat": "^0.4.2",
|
"@coingecko/cryptoformat": "^0.4.2",
|
||||||
"@loadable/component": "^5.15.0",
|
"@loadable/component": "^5.15.0",
|
||||||
"@oceanprotocol/art": "^3.0.0",
|
"@oceanprotocol/art": "^3.0.0",
|
||||||
@ -32,6 +33,7 @@
|
|||||||
"@portis/web3": "^4.0.4",
|
"@portis/web3": "^4.0.4",
|
||||||
"@sindresorhus/slugify": "^2.1.0",
|
"@sindresorhus/slugify": "^2.1.0",
|
||||||
"@tippyjs/react": "^4.2.5",
|
"@tippyjs/react": "^4.2.5",
|
||||||
|
"@urql/introspection": "^0.3.0",
|
||||||
"@walletconnect/web3-provider": "^1.5.0-rc.7",
|
"@walletconnect/web3-provider": "^1.5.0-rc.7",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
@ -60,6 +62,7 @@
|
|||||||
"gatsby-transformer-remark": "^2.16.1",
|
"gatsby-transformer-remark": "^2.16.1",
|
||||||
"gatsby-transformer-sharp": "^2.12.1",
|
"gatsby-transformer-sharp": "^2.12.1",
|
||||||
"graphql": "14.7.0",
|
"graphql": "14.7.0",
|
||||||
|
"graphql-schema-typescript": "^1.5.2",
|
||||||
"is-url-superb": "^6.0.0",
|
"is-url-superb": "^6.0.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
@ -81,12 +84,18 @@
|
|||||||
"shortid": "^2.2.16",
|
"shortid": "^2.2.16",
|
||||||
"slugify": "^1.5.3",
|
"slugify": "^1.5.3",
|
||||||
"swr": "^0.5.6",
|
"swr": "^0.5.6",
|
||||||
|
"urql": "^2.0.3",
|
||||||
"use-dark-mode": "^2.3.1",
|
"use-dark-mode": "^2.3.1",
|
||||||
"web3": "^1.4.0",
|
"web3": "^1.4.0",
|
||||||
"web3modal": "^1.9.3",
|
"web3modal": "^1.9.3",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"@testing-library/jest-dom": "^5.12.0",
|
"@testing-library/jest-dom": "^5.12.0",
|
||||||
"@testing-library/react": "^11.2.7",
|
"@testing-library/react": "^11.2.7",
|
||||||
|
1
src/@types/ComputeJobMetaData.d.ts
vendored
1
src/@types/ComputeJobMetaData.d.ts
vendored
@ -3,4 +3,5 @@ import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Comput
|
|||||||
export interface ComputeJobMetaData extends ComputeJob {
|
export interface ComputeJobMetaData extends ComputeJob {
|
||||||
assetName: string
|
assetName: string
|
||||||
assetDtSymbol: string
|
assetDtSymbol: string
|
||||||
|
networkId: number
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,9 @@ import Styles from '../global/Styles'
|
|||||||
import { useWeb3 } from '../providers/Web3'
|
import { useWeb3 } from '../providers/Web3'
|
||||||
import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
||||||
import { useAccountPurgatory } from '../hooks/useAccountPurgatory'
|
import { useAccountPurgatory } from '../hooks/useAccountPurgatory'
|
||||||
import NetworkBanner from './molecules/NetworkBanner'
|
|
||||||
import styles from './App.module.css'
|
|
||||||
import AnnouncementBanner from './atoms/AnnouncementBanner'
|
import AnnouncementBanner from './atoms/AnnouncementBanner'
|
||||||
import { useGraphSyncStatus } from '../hooks/useGraphSyncStatus'
|
import { useGraphSyncStatus } from '../hooks/useGraphSyncStatus'
|
||||||
|
import styles from './App.module.css'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query AppQuery {
|
query AppQuery {
|
||||||
@ -41,24 +40,15 @@ export default function App({
|
|||||||
const { warning } = useSiteMetadata()
|
const { warning } = useSiteMetadata()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
|
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
|
||||||
const { isGraphSynced, blockHead, blockGraph } = useGraphSyncStatus()
|
// const { isGraphSynced, blockHead, blockGraph } = useGraphSyncStatus()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styles>
|
<Styles>
|
||||||
<div className={styles.app}>
|
<div className={styles.app}>
|
||||||
{!isGraphSynced && (
|
|
||||||
<AnnouncementBanner
|
|
||||||
text={`The data for this network has only synced to Ethereum block ${blockGraph} (out of ${blockHead}). Please check back soon.`}
|
|
||||||
state="error"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!location.pathname.includes('/asset/did') && <NetworkBanner />}
|
|
||||||
|
|
||||||
<Header />
|
|
||||||
|
|
||||||
{(props as PageProps).uri === '/' && (
|
{(props as PageProps).uri === '/' && (
|
||||||
<Alert text={warning.main} state="info" />
|
<AnnouncementBanner text={warning.main} />
|
||||||
)}
|
)}
|
||||||
|
<Header />
|
||||||
|
|
||||||
{isInPurgatory && (
|
{isInPurgatory && (
|
||||||
<Alert
|
<Alert
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
.typeLabel {
|
.typeLabel {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border-right: 1px solid var(--border-color);
|
border-left: 1px solid var(--border-color);
|
||||||
padding-right: calc(var(--spacer) / 3.5);
|
padding-left: calc(var(--spacer) / 3.5);
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-left: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,6 @@ export default function AssetType({
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div className={styleClasses}>
|
<div className={styleClasses}>
|
||||||
<div className={styles.typeLabel}>
|
|
||||||
{type === 'dataset' ? 'data set' : 'algorithm'}
|
|
||||||
</div>
|
|
||||||
{accessType === 'access' ? (
|
{accessType === 'access' ? (
|
||||||
<Download role="img" aria-label="Download" className={styles.icon} />
|
<Download role="img" aria-label="Download" className={styles.icon} />
|
||||||
) : accessType === 'compute' && type === 'algorithm' ? (
|
) : accessType === 'compute' && type === 'algorithm' ? (
|
||||||
@ -31,6 +28,10 @@ export default function AssetType({
|
|||||||
) : (
|
) : (
|
||||||
<Compute role="img" aria-label="Compute" className={styles.icon} />
|
<Compute role="img" aria-label="Compute" className={styles.icon} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className={styles.typeLabel}>
|
||||||
|
{type === 'dataset' ? 'data set' : 'algorithm'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
box-shadow: 0 6px 17px 0 var(--box-shadow-color);
|
box-shadow: 0 6px 17px 0 var(--box-shadow-color);
|
||||||
overflow: hidden;
|
|
||||||
padding: calc(var(--spacer) / 1.5);
|
padding: calc(var(--spacer) / 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
color: var(--brand-pink);
|
color: var(--brand-pink);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
min-width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Size Modifiers */
|
/* Size Modifiers */
|
||||||
|
@ -4,10 +4,12 @@ import classNames from 'classnames/bind'
|
|||||||
import { ConfigHelperConfig } from '@oceanprotocol/lib'
|
import { ConfigHelperConfig } from '@oceanprotocol/lib'
|
||||||
import { useOcean } from '../../providers/Ocean'
|
import { useOcean } from '../../providers/Ocean'
|
||||||
import styles from './ExplorerLink.module.css'
|
import styles from './ExplorerLink.module.css'
|
||||||
|
import { getOceanConfig } from '../../utils/ocean'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function ExplorerLink({
|
export default function ExplorerLink({
|
||||||
|
networkId,
|
||||||
path,
|
path,
|
||||||
children,
|
children,
|
||||||
className
|
className
|
||||||
@ -17,22 +19,29 @@ export default function ExplorerLink({
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { config } = useOcean()
|
const { config, ocean } = useOcean()
|
||||||
const [url, setUrl] = useState<string>()
|
const [url, setUrl] = useState<string>()
|
||||||
|
const [oceanConfig, setOceanConfig] = useState<ConfigHelperConfig>()
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
link: true,
|
link: true,
|
||||||
[className]: className
|
[className]: className
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUrl((config as ConfigHelperConfig).explorerUri)
|
async function initOcean() {
|
||||||
}, [config])
|
const oceanInitialConfig = getOceanConfig(networkId)
|
||||||
|
setOceanConfig(oceanInitialConfig)
|
||||||
|
setUrl(oceanInitialConfig?.explorerUri)
|
||||||
|
}
|
||||||
|
if (oceanConfig === undefined) {
|
||||||
|
initOcean()
|
||||||
|
}
|
||||||
|
}, [config, networkId, ocean])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={`${url}/${path}`}
|
href={`${url}/${path}`}
|
||||||
title={`View on ${(config as ConfigHelperConfig).explorerUri}`}
|
title={`View on ${oceanConfig?.explorerUri}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={styleClasses}
|
className={styleClasses}
|
||||||
|
@ -27,6 +27,13 @@ export interface InputProps {
|
|||||||
| ChangeEvent<HTMLSelectElement>
|
| ChangeEvent<HTMLSelectElement>
|
||||||
| ChangeEvent<HTMLTextAreaElement>
|
| ChangeEvent<HTMLTextAreaElement>
|
||||||
): void
|
): void
|
||||||
|
onKeyPress?(
|
||||||
|
e:
|
||||||
|
| React.KeyboardEvent<HTMLInputElement>
|
||||||
|
| React.KeyboardEvent<HTMLInputElement>
|
||||||
|
| React.KeyboardEvent<HTMLSelectElement>
|
||||||
|
| React.KeyboardEvent<HTMLTextAreaElement>
|
||||||
|
): void
|
||||||
rows?: number
|
rows?: number
|
||||||
multiple?: boolean
|
multiple?: boolean
|
||||||
pattern?: string
|
pattern?: string
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.logo {
|
.logo {
|
||||||
width: 4rem;
|
width: 2.5rem;
|
||||||
height: 4rem;
|
height: 2.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
src/components/atoms/NetworkName.module.css
Normal file
28
src/components/atoms/NetworkName.module.css
Normal file
@ -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;
|
||||||
|
}
|
48
src/components/atoms/NetworkName.tsx
Normal file
48
src/components/atoms/NetworkName.tsx
Normal file
@ -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 ? <IconMapped className={styles.icon} /> : 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 (
|
||||||
|
<span
|
||||||
|
className={`${styles.network} ${minimal ? styles.minimal : null} ${
|
||||||
|
className || ''
|
||||||
|
}`}
|
||||||
|
title={networkName}
|
||||||
|
>
|
||||||
|
<NetworkIcon name={networkName} />{' '}
|
||||||
|
<span className={styles.name}>{networkName}</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
@ -6,6 +6,9 @@
|
|||||||
font-weight: var(--font-weight-base);
|
font-weight: var(--font-weight-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.removeTvlPadding {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
/* fiat currency symbol */
|
/* fiat currency symbol */
|
||||||
.conversion strong span {
|
.conversion strong span {
|
||||||
font-weight: var(--font-weight-base);
|
font-weight: var(--font-weight-base);
|
||||||
|
@ -10,11 +10,13 @@ const cx = classNames.bind(styles)
|
|||||||
export default function Conversion({
|
export default function Conversion({
|
||||||
price,
|
price,
|
||||||
className,
|
className,
|
||||||
hideApproximateSymbol
|
hideApproximateSymbol,
|
||||||
|
showTVLLabel
|
||||||
}: {
|
}: {
|
||||||
price: string // expects price in OCEAN, not wei
|
price: string // expects price in OCEAN, not wei
|
||||||
className?: string
|
className?: string
|
||||||
hideApproximateSymbol?: boolean
|
hideApproximateSymbol?: boolean
|
||||||
|
showTVLLabel?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { prices } = usePrices()
|
const { prices } = usePrices()
|
||||||
const { currency, locale } = useUserPreferences()
|
const { currency, locale } = useUserPreferences()
|
||||||
@ -27,6 +29,7 @@ export default function Conversion({
|
|||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
conversion: true,
|
conversion: true,
|
||||||
|
removeTvlPadding: showTVLLabel,
|
||||||
[className]: className
|
[className]: className
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -61,6 +64,7 @@ export default function Conversion({
|
|||||||
className={styleClasses}
|
className={styleClasses}
|
||||||
title="Approximation based on current OCEAN spot price on Coingecko"
|
title="Approximation based on current OCEAN spot price on Coingecko"
|
||||||
>
|
>
|
||||||
|
{showTVLLabel && 'TVL'}
|
||||||
{!hideApproximateSymbol && '≈ '}
|
{!hideApproximateSymbol && '≈ '}
|
||||||
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
||||||
{!isFiat && currency}
|
{!isFiat && currency}
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1rem;
|
width: 1em;
|
||||||
height: 1rem;
|
height: 1em;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: -0.1rem;
|
margin-bottom: -0.1em;
|
||||||
margin-left: calc(var(--spacer) / 6);
|
margin-left: calc(var(--spacer) / 6);
|
||||||
fill: var(--color-secondary);
|
fill: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,7 @@ export default function Tooltip({
|
|||||||
trigger,
|
trigger,
|
||||||
disabled,
|
disabled,
|
||||||
className,
|
className,
|
||||||
placement,
|
placement
|
||||||
link,
|
|
||||||
reference
|
|
||||||
}: {
|
}: {
|
||||||
content: ReactNode
|
content: ReactNode
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
@ -38,8 +36,6 @@ export default function Tooltip({
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
placement?: Placement
|
placement?: Placement
|
||||||
link?: string
|
|
||||||
reference?: string
|
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [props, setSpring] = useSpring(() => animation.from)
|
const [props, setSpring] = useSpring(() => animation.from)
|
||||||
|
|
||||||
@ -76,7 +72,6 @@ export default function Tooltip({
|
|||||||
<animated.div style={props}>
|
<animated.div style={props}>
|
||||||
<div className={styles.content} {...attrs}>
|
<div className={styles.content} {...attrs}>
|
||||||
{content}
|
{content}
|
||||||
{link && <a href={link}>{reference}</a>}
|
|
||||||
<div className={styles.arrow} data-popper-arrow />
|
<div className={styles.arrow} data-popper-arrow />
|
||||||
</div>
|
</div>
|
||||||
</animated.div>
|
</animated.div>
|
||||||
|
@ -5,6 +5,7 @@ import React, { ReactElement, useEffect, useState } from 'react'
|
|||||||
import { getAssetsNames } from '../../utils/aquarius'
|
import { getAssetsNames } from '../../utils/aquarius'
|
||||||
import styles from './AssetListTitle.module.css'
|
import styles from './AssetListTitle.module.css'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||||
|
|
||||||
export default function AssetListTitle({
|
export default function AssetListTitle({
|
||||||
ddo,
|
ddo,
|
||||||
@ -15,11 +16,11 @@ export default function AssetListTitle({
|
|||||||
did?: string
|
did?: string
|
||||||
title?: string
|
title?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { config } = useOcean()
|
const { appConfig } = useSiteMetadata()
|
||||||
const [assetTitle, setAssetTitle] = useState<string>(title)
|
const [assetTitle, setAssetTitle] = useState<string>(title)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (title || !config?.metadataCacheUri) return
|
if (title || !appConfig.metadataCacheUri) return
|
||||||
if (ddo) {
|
if (ddo) {
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
setAssetTitle(attributes.main.name)
|
setAssetTitle(attributes.main.name)
|
||||||
@ -29,11 +30,7 @@ export default function AssetListTitle({
|
|||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
async function getAssetName() {
|
async function getAssetName() {
|
||||||
const title = await getAssetsNames(
|
const title = await getAssetsNames([did], source.token)
|
||||||
[did],
|
|
||||||
config.metadataCacheUri,
|
|
||||||
source.token
|
|
||||||
)
|
|
||||||
setAssetTitle(title[did])
|
setAssetTitle(title[did])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +39,7 @@ export default function AssetListTitle({
|
|||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [assetTitle, config?.metadataCacheUri, ddo, did, title])
|
}, [assetTitle, appConfig.metadataCacheUri, ddo, did, title])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h3 className={styles.title}>
|
<h3 className={styles.title}>
|
||||||
|
@ -57,11 +57,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
margin-top: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.typeDetails {
|
.typeDetails {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(var(--spacer) / 3);
|
top: calc(var(--spacer) / 3);
|
||||||
@ -69,3 +64,10 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
font-size: var(--font-size-mini);
|
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);
|
||||||
|
}
|
||||||
|
@ -2,12 +2,13 @@ import React from 'react'
|
|||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
import Dotdotdot from 'react-dotdotdot'
|
||||||
import Price from '../atoms/Price'
|
import Price from '../atoms/Price'
|
||||||
import styles from './AssetTeaser.module.css'
|
|
||||||
import { DDO, BestPrice } from '@oceanprotocol/lib'
|
import { DDO, BestPrice } from '@oceanprotocol/lib'
|
||||||
import removeMarkdown from 'remove-markdown'
|
import removeMarkdown from 'remove-markdown'
|
||||||
import Publisher from '../atoms/Publisher'
|
import Publisher from '../atoms/Publisher'
|
||||||
import Time from '../atoms/Time'
|
|
||||||
import AssetType from '../atoms/AssetType'
|
import AssetType from '../atoms/AssetType'
|
||||||
|
import NetworkName from '../atoms/NetworkName'
|
||||||
|
import { useOcean } from '../../providers/Ocean'
|
||||||
|
import styles from './AssetTeaser.module.css'
|
||||||
|
|
||||||
declare type AssetTeaserProps = {
|
declare type AssetTeaserProps = {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
@ -52,9 +53,7 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
|||||||
|
|
||||||
<footer className={styles.foot}>
|
<footer className={styles.foot}>
|
||||||
<Price price={price} small />
|
<Price price={price} small />
|
||||||
<p className={styles.date}>
|
<NetworkName networkId={ddo.chainId} className={styles.network} />
|
||||||
<Time date={ddo?.created} relative />
|
|
||||||
</p>
|
|
||||||
</footer>
|
</footer>
|
||||||
</Link>
|
</Link>
|
||||||
</article>
|
</article>
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
import { useUserPreferences } from '../../providers/UserPreferences'
|
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import Table from '../atoms/Table'
|
import Table from '../atoms/Table'
|
||||||
import { DDO, Logger, ConfigHelperConfig } from '@oceanprotocol/lib'
|
import { DDO, Logger, BestPrice } from '@oceanprotocol/lib'
|
||||||
import { useOcean } from '../../providers/Ocean'
|
|
||||||
import Price from '../atoms/Price'
|
import Price from '../atoms/Price'
|
||||||
import Tooltip from '../atoms/Tooltip'
|
import Tooltip from '../atoms/Tooltip'
|
||||||
import AssetTitle from './AssetListTitle'
|
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 axios, { CancelToken } from 'axios'
|
||||||
|
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||||
|
|
||||||
async function getAssetsBookmarked(
|
async function getAssetsBookmarked(
|
||||||
bookmarks: string[],
|
bookmarks: string[],
|
||||||
metadataCacheUri: string,
|
chainIds: number[],
|
||||||
cancelToken: CancelToken
|
cancelToken: CancelToken
|
||||||
) {
|
) {
|
||||||
const searchDids = JSON.stringify(bookmarks)
|
const searchDids = JSON.stringify(bookmarks)
|
||||||
@ -26,7 +30,9 @@ async function getAssetsBookmarked(
|
|||||||
offset: 100,
|
offset: 100,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
query_string: {
|
||||||
query: searchDids,
|
query: `(${searchDids}) AND (${transformChainIdsListToQuery(
|
||||||
|
chainIds
|
||||||
|
)})`,
|
||||||
fields: ['dataToken'],
|
fields: ['dataToken'],
|
||||||
default_operator: 'OR'
|
default_operator: 'OR'
|
||||||
}
|
}
|
||||||
@ -35,11 +41,7 @@ async function getAssetsBookmarked(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await queryMetadata(
|
const result = await queryMetadata(queryBookmarks, cancelToken)
|
||||||
queryBookmarks,
|
|
||||||
metadataCacheUri,
|
|
||||||
cancelToken
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -50,19 +52,19 @@ async function getAssetsBookmarked(
|
|||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'Data Set',
|
name: 'Data Set',
|
||||||
selector: function getAssetRow(row: DDO) {
|
selector: function getAssetRow(row: AssetListPrices) {
|
||||||
const { attributes } = row.findServiceByType('metadata')
|
const { attributes } = row.ddo.findServiceByType('metadata')
|
||||||
return <AssetTitle title={attributes.main.name} ddo={row} />
|
return <AssetTitle title={attributes.main.name} ddo={row.ddo} />
|
||||||
},
|
},
|
||||||
maxWidth: '45rem',
|
maxWidth: '45rem',
|
||||||
grow: 1
|
grow: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Datatoken Symbol',
|
name: 'Datatoken Symbol',
|
||||||
selector: function getAssetRow(row: DDO) {
|
selector: function getAssetRow(row: AssetListPrices) {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={row.dataTokenInfo.name}>
|
<Tooltip content={row.ddo.dataTokenInfo.name}>
|
||||||
{row.dataTokenInfo.symbol}
|
{row.ddo.dataTokenInfo.symbol}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -70,7 +72,7 @@ const columns = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Price',
|
name: 'Price',
|
||||||
selector: function getAssetRow(row: DDO) {
|
selector: function getAssetRow(row: AssetListPrices) {
|
||||||
return <Price price={row.price} small />
|
return <Price price={row.price} small />
|
||||||
},
|
},
|
||||||
right: true
|
right: true
|
||||||
@ -78,21 +80,20 @@ const columns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default function Bookmarks(): ReactElement {
|
export default function Bookmarks(): ReactElement {
|
||||||
const { config } = useOcean()
|
const { appConfig } = useSiteMetadata()
|
||||||
const { bookmarks } = useUserPreferences()
|
const { bookmarks } = useUserPreferences()
|
||||||
|
|
||||||
const [pinned, setPinned] = useState<DDO[]>()
|
const [pinned, setPinned] = useState<AssetListPrices[]>()
|
||||||
const [isLoading, setIsLoading] = useState<boolean>()
|
const [isLoading, setIsLoading] = useState<boolean>()
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
const networkName = (config as ConfigHelperConfig)?.network
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!config?.metadataCacheUri || !networkName || bookmarks === {}) return
|
if (!appConfig.metadataCacheUri || bookmarks === []) return
|
||||||
|
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
if (!bookmarks[networkName]?.length) {
|
if (!bookmarks?.length) {
|
||||||
setPinned([])
|
setPinned([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -101,11 +102,14 @@ export default function Bookmarks(): ReactElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const resultPinned = await getAssetsBookmarked(
|
const resultPinned = await getAssetsBookmarked(
|
||||||
bookmarks[networkName],
|
bookmarks,
|
||||||
config.metadataCacheUri,
|
chainIds,
|
||||||
source.token
|
source.token
|
||||||
)
|
)
|
||||||
setPinned(resultPinned?.results)
|
const pinnedAssets: AssetListPrices[] = await getAssetsBestPrices(
|
||||||
|
resultPinned?.results
|
||||||
|
)
|
||||||
|
setPinned(pinnedAssets)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error.message)
|
Logger.error(error.message)
|
||||||
}
|
}
|
||||||
@ -117,7 +121,7 @@ export default function Bookmarks(): ReactElement {
|
|||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [bookmarks, config.metadataCacheUri, networkName])
|
}, [bookmarks, chainIds])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
|
@ -4,25 +4,27 @@ import { useField } from 'formik'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import FileInfo from './Info'
|
import FileInfo from './Info'
|
||||||
import FileInput from './Input'
|
import FileInput from './Input'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
|
||||||
import { InputProps } from '../../../atoms/Input'
|
import { InputProps } from '../../../atoms/Input'
|
||||||
import { fileinfo } from '../../../../utils/provider'
|
import { fileinfo } from '../../../../utils/provider'
|
||||||
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
import { getOceanConfig } from '../../../../utils/ocean'
|
||||||
|
|
||||||
export default function FilesInput(props: InputProps): ReactElement {
|
export default function FilesInput(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [fileUrl, setFileUrl] = useState<string>()
|
const [fileUrl, setFileUrl] = useState<string>()
|
||||||
const { config } = useOcean()
|
const { chainId } = useWeb3()
|
||||||
|
|
||||||
function loadFileInfo() {
|
function loadFileInfo() {
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
const config = getOceanConfig(chainId || 1)
|
||||||
|
|
||||||
async function validateUrl() {
|
async function validateUrl() {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const checkedFile = await fileinfo(
|
const checkedFile = await fileinfo(
|
||||||
fileUrl,
|
fileUrl,
|
||||||
config.providerUri,
|
config?.providerUri,
|
||||||
source.token
|
source.token
|
||||||
)
|
)
|
||||||
checkedFile && helpers.setValue([checkedFile])
|
checkedFile && helpers.setValue([checkedFile])
|
||||||
@ -43,7 +45,7 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFileInfo()
|
loadFileInfo()
|
||||||
}, [fileUrl, config.providerUri])
|
}, [fileUrl])
|
||||||
|
|
||||||
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
|
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
|
||||||
// hack so the onBlur-triggered validation does not show,
|
// hack so the onBlur-triggered validation does not show,
|
||||||
|
@ -1,18 +1,42 @@
|
|||||||
.stats {
|
.stats {
|
||||||
margin-bottom: calc(var(--spacer) * 2);
|
margin-bottom: calc(var(--spacer) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* specificity sledgehammer override without !important */
|
||||||
|
.stats,
|
||||||
|
.stats *,
|
||||||
|
.statsList * {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
line-height: 2;
|
color: var(--color-secondary);
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats > div > div {
|
.tooltipStats {
|
||||||
display: inline-block;
|
margin-bottom: calc(var(--spacer) / 3);
|
||||||
|
padding-bottom: calc(var(--spacer) / 3);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.total {
|
.network {
|
||||||
color: var(--color-secondary) !important;
|
font-weight: var(--font-weight-bold);
|
||||||
font-size: var(--font-size-small) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
width: 0.85rem;
|
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);
|
||||||
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import styles from './MarketStats.module.css'
|
import { gql, OperationContext } from 'urql'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
|
||||||
import Conversion from '../atoms/Price/Conversion'
|
import Conversion from '../atoms/Price/Conversion'
|
||||||
import PriceUnit from '../atoms/Price/PriceUnit'
|
import PriceUnit from '../atoms/Price/PriceUnit'
|
||||||
import Tooltip from '../atoms/Tooltip'
|
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`
|
const getTotalPoolsValues = gql`
|
||||||
query PoolsData {
|
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 (
|
||||||
|
<>
|
||||||
|
<Conversion price={totalValueLocked} hideApproximateSymbol />{' '}
|
||||||
|
<abbr title="Total Value Locked">TVL</abbr> across{' '}
|
||||||
|
<strong>{poolCount}</strong> asset pools that contain{' '}
|
||||||
|
<PriceUnit price={totalOceanLiquidity} small className={styles.total} />,
|
||||||
|
plus datatokens for each pool.
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MarketNetworkStatsTooltip({
|
||||||
|
totalValueLocked,
|
||||||
|
poolCount,
|
||||||
|
totalOceanLiquidity,
|
||||||
|
mainChainIds
|
||||||
|
}: {
|
||||||
|
totalValueLocked: Value
|
||||||
|
poolCount: Value
|
||||||
|
totalOceanLiquidity: Value
|
||||||
|
mainChainIds: number[]
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ul className={styles.statsList}>
|
||||||
|
{totalValueLocked &&
|
||||||
|
totalOceanLiquidity &&
|
||||||
|
poolCount &&
|
||||||
|
mainChainIds?.map((chainId, key) => (
|
||||||
|
<li className={styles.tooltipStats} key={key}>
|
||||||
|
<NetworkName networkId={chainId} className={styles.network} />
|
||||||
|
<br />
|
||||||
|
<Conversion
|
||||||
|
price={totalValueLocked[chainId] || '0'}
|
||||||
|
hideApproximateSymbol
|
||||||
|
/>{' '}
|
||||||
|
<abbr title="Total Value Locked">TVL</abbr>
|
||||||
|
{' | '}
|
||||||
|
<strong>{poolCount[chainId] || '0'}</strong> pools
|
||||||
|
{' | '}
|
||||||
|
<PriceUnit price={totalOceanLiquidity[chainId] || '0'} small />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<p className={styles.note}>
|
||||||
|
Counted on-chain from our pool factory. Does not filter out assets in{' '}
|
||||||
|
<a href="https://github.com/oceanprotocol/list-purgatory">
|
||||||
|
list-purgatory
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function MarketStats(): ReactElement {
|
export default function MarketStats(): ReactElement {
|
||||||
const [totalValueLocked, setTotalValueLocked] = useState<string>()
|
const [totalValueLocked, setTotalValueLocked] = useState<Value>()
|
||||||
const [totalOceanLiquidity, setTotalOceanLiquidity] = useState<string>()
|
const [totalOceanLiquidity, setTotalOceanLiquidity] = useState<Value>()
|
||||||
const [poolCount, setPoolCount] = useState<number>()
|
const [poolCount, setPoolCount] = useState<Value>()
|
||||||
const { data } = useQuery(getTotalPoolsValues, { pollInterval: 20000 })
|
const [totalValueLockedSum, setTotalValueLockedSum] = useState<string>()
|
||||||
|
const [totalOceanLiquiditySum, setTotalOceanLiquiditySum] = useState<string>()
|
||||||
|
const [poolCountSum, setPoolCountSum] = useState<string>()
|
||||||
|
const [mainChainIds, setMainChainIds] = useState<number[]>()
|
||||||
|
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(() => {
|
useEffect(() => {
|
||||||
if (!data || !data.poolFactories || data.poolFactories.length === 0) return
|
getMarketStats()
|
||||||
setTotalValueLocked(data.poolFactories[0].totalValueLocked)
|
}, [])
|
||||||
setTotalOceanLiquidity(data.poolFactories[0].totalOceanLiquidity)
|
|
||||||
setPoolCount(data.poolFactories[0].finalizedPoolCount)
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.stats}>
|
<div className={styles.stats}>
|
||||||
<Conversion price={`${totalValueLocked}`} hideApproximateSymbol />{' '}
|
<>
|
||||||
<abbr title="Total Value Locked">TVL</abbr> across{' '}
|
<MarketNetworkStats
|
||||||
<strong>{poolCount}</strong> data set pools that contain{' '}
|
totalValueLocked={totalValueLockedSum || '0'}
|
||||||
<PriceUnit price={totalOceanLiquidity} small className={styles.total} />,
|
totalOceanLiquidity={totalOceanLiquiditySum || '0'}
|
||||||
plus datatokens for each pool.
|
poolCount={poolCountSum || '0'}
|
||||||
<Tooltip
|
/>{' '}
|
||||||
className={styles.info}
|
<Tooltip
|
||||||
content="Counted on-chain from our pool factory. Does not filter out data sets in "
|
className={styles.info}
|
||||||
reference="list-purgatory"
|
content={
|
||||||
link="https://github.com/oceanprotocol/list-purgatory"
|
<MarketNetworkStatsTooltip
|
||||||
/>
|
totalValueLocked={totalValueLocked}
|
||||||
|
poolCount={poolCount}
|
||||||
|
totalOceanLiquidity={totalOceanLiquidity}
|
||||||
|
mainChainIds={mainChainIds}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,66 @@
|
|||||||
.menu {
|
.menu {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
padding: calc(var(--spacer) / 2);
|
||||||
|
|
||||||
.menu > div {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
flex-wrap: wrap;
|
||||||
padding-top: calc(var(--spacer) / 2);
|
|
||||||
padding-bottom: calc(var(--spacer) / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoUnit {
|
.logo {
|
||||||
|
order: 1;
|
||||||
|
white-space: nowrap;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoUnit svg {
|
.navigation {
|
||||||
margin-left: -0.5rem;
|
order: 3;
|
||||||
margin-right: 0.5rem;
|
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 {
|
.title {
|
||||||
display: none;
|
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 {
|
.title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
margin-right: var(--spacer);
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-size: var(--font-size-h4);
|
font-size: var(--font-size-h4);
|
||||||
@ -38,6 +72,7 @@
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
-ms-overflow-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation::-webkit-scrollbar,
|
.navigation::-webkit-scrollbar,
|
||||||
@ -48,19 +83,13 @@
|
|||||||
.navigation li {
|
.navigation li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
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 {
|
.link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: calc(var(--spacer) / 2);
|
padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
@ -69,16 +98,11 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation button {
|
.actions,
|
||||||
text-transform: none;
|
|
||||||
padding-top: calc(var(--spacer) / 4);
|
|
||||||
padding-bottom: calc(var(--spacer) / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover,
|
.link:hover,
|
||||||
.link:focus,
|
.link:focus,
|
||||||
.link:active {
|
.link:active {
|
||||||
color: var(--brand-grey);
|
color: var(--font-color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link[aria-current],
|
.link[aria-current],
|
||||||
@ -90,3 +114,11 @@
|
|||||||
.link:last-child {
|
.link:last-child {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo svg {
|
||||||
|
margin-right: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
@ -4,10 +4,11 @@ import { useLocation } from '@reach/router'
|
|||||||
import loadable from '@loadable/component'
|
import loadable from '@loadable/component'
|
||||||
import styles from './Menu.module.css'
|
import styles from './Menu.module.css'
|
||||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||||
import Container from '../atoms/Container'
|
|
||||||
import UserPreferences from './UserPreferences'
|
import UserPreferences from './UserPreferences'
|
||||||
import Badge from '../atoms/Badge'
|
import Badge from '../atoms/Badge'
|
||||||
import Logo from '../atoms/Logo'
|
import Logo from '../atoms/Logo'
|
||||||
|
import Networks from './UserPreferences/Networks'
|
||||||
|
import SearchBar from './SearchBar'
|
||||||
|
|
||||||
const Wallet = loadable(() => import('./Wallet'))
|
const Wallet = loadable(() => import('./Wallet'))
|
||||||
|
|
||||||
@ -36,28 +37,27 @@ export default function Menu(): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.menu}>
|
<nav className={styles.menu}>
|
||||||
<Container>
|
<Link to="/" className={styles.logo}>
|
||||||
<Link to="/" className={styles.logoUnit}>
|
<Logo noWordmark />
|
||||||
<Logo />
|
<h1 className={styles.title}>
|
||||||
<h1 className={styles.title}>
|
{siteTitle} <Badge label="v3" />
|
||||||
{siteTitle} <Badge label="beta" />
|
</h1>
|
||||||
</h1>
|
</Link>
|
||||||
</Link>
|
|
||||||
|
|
||||||
<ul className={styles.navigation}>
|
<ul className={styles.navigation}>
|
||||||
{menu.map((item: MenuItem) => (
|
{menu.map((item: MenuItem) => (
|
||||||
<li key={item.name}>
|
<li key={item.name}>
|
||||||
<MenuLink item={item} />
|
<MenuLink item={item} />
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
<li>
|
|
||||||
<Wallet />
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
))}
|
||||||
<UserPreferences />
|
</ul>
|
||||||
</li>
|
|
||||||
</ul>
|
<div className={styles.actions}>
|
||||||
</Container>
|
<SearchBar />
|
||||||
|
<Networks />
|
||||||
|
<Wallet />
|
||||||
|
<UserPreferences />
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.datatoken {
|
.datatoken {
|
||||||
margin-top: calc(var(--spacer) / 8);
|
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { File as FileMetadata } from '@oceanprotocol/lib/dist/node/ddo/interface
|
|||||||
import Markdown from '../atoms/Markdown'
|
import Markdown from '../atoms/Markdown'
|
||||||
import Tags from '../atoms/Tags'
|
import Tags from '../atoms/Tags'
|
||||||
import MetaItem from '../organisms/AssetContent/MetaItem'
|
import MetaItem from '../organisms/AssetContent/MetaItem'
|
||||||
import styles from './MetadataPreview.module.css'
|
|
||||||
import File from '../atoms/File'
|
import File from '../atoms/File'
|
||||||
import {
|
import {
|
||||||
MetadataPublishFormDataset,
|
MetadataPublishFormDataset,
|
||||||
@ -11,6 +10,11 @@ import {
|
|||||||
} from '../../@types/MetaData'
|
} from '../../@types/MetaData'
|
||||||
import Button from '../atoms/Button'
|
import Button from '../atoms/Button'
|
||||||
import { transformTags } from '../../utils/metadata'
|
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 }) {
|
function Description({ description }: { description: string }) {
|
||||||
const [fullDescription, setFullDescription] = useState<boolean>(false)
|
const [fullDescription, setFullDescription] = useState<boolean>(false)
|
||||||
@ -92,10 +96,14 @@ export function MetadataPreview({
|
|||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishFormDataset>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const { networkId } = useWeb3()
|
||||||
|
const { isAssetNetwork } = useAsset()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.preview}>
|
<div className={styles.preview}>
|
||||||
<h2 className={styles.previewTitle}>Preview</h2>
|
<h2 className={styles.previewTitle}>Preview</h2>
|
||||||
<header>
|
<header>
|
||||||
|
{networkId && <NetworkName networkId={networkId} />}
|
||||||
{values.name && <h3 className={styles.title}>{values.name}</h3>}
|
{values.name && <h3 className={styles.title}>{values.name}</h3>}
|
||||||
{values.dataTokenOptions?.name && (
|
{values.dataTokenOptions?.name && (
|
||||||
<p
|
<p
|
||||||
@ -121,6 +129,9 @@ export function MetadataPreview({
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<MetaFull values={values} />
|
<MetaFull values={values} />
|
||||||
|
{isAssetNetwork === false && (
|
||||||
|
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -130,10 +141,13 @@ export function MetadataAlgorithmPreview({
|
|||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishFormAlgorithm>
|
values: Partial<MetadataPublishFormAlgorithm>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const { networkId } = useWeb3()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.preview}>
|
<div className={styles.preview}>
|
||||||
<h2 className={styles.previewTitle}>Preview</h2>
|
<h2 className={styles.previewTitle}>Preview</h2>
|
||||||
<header>
|
<header>
|
||||||
|
{networkId && <NetworkName networkId={networkId} />}
|
||||||
{values.name && <h3 className={styles.title}>{values.name}</h3>}
|
{values.name && <h3 className={styles.title}>{values.name}</h3>}
|
||||||
{values.dataTokenOptions?.name && (
|
{values.dataTokenOptions?.name && (
|
||||||
<p
|
<p
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import { useWeb3 } from '../../providers/Web3'
|
|
||||||
import { addCustomNetwork, NetworkObject } from '../../utils/web3'
|
|
||||||
import { getOceanConfig } from '../../utils/ocean'
|
|
||||||
import { useOcean } from '../../providers/Ocean'
|
|
||||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
|
||||||
import AnnouncementBanner, {
|
|
||||||
AnnouncementAction
|
|
||||||
} from '../atoms/AnnouncementBanner'
|
|
||||||
|
|
||||||
const networkMatic: NetworkObject = {
|
|
||||||
chainId: 137,
|
|
||||||
name: 'Matic Network',
|
|
||||||
urlList: [
|
|
||||||
'https://rpc-mainnet.matic.network',
|
|
||||||
'https://rpc-mainnet.maticvigil.com/'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NetworkBanner(): ReactElement {
|
|
||||||
const { web3Provider, web3ProviderInfo } = useWeb3()
|
|
||||||
const { config, connect } = useOcean()
|
|
||||||
const { announcement } = useSiteMetadata()
|
|
||||||
|
|
||||||
const [text, setText] = useState<string>(announcement.main)
|
|
||||||
const [action, setAction] = useState<AnnouncementAction>()
|
|
||||||
|
|
||||||
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 <AnnouncementBanner text={text} action={action} />
|
|
||||||
}
|
|
@ -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<string>()
|
|
||||||
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 ? (
|
|
||||||
<ExplorerLink networkId={networkId} path={`/tx/${row.tx}`}>
|
|
||||||
<span className={styles.titleText}>{title}</span>
|
|
||||||
</ExplorerLink>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Title',
|
|
||||||
selector: function getTitleRow(row: TransactionHistoryPoolTransactions) {
|
|
||||||
return <Title row={row} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,3 @@
|
|||||||
|
.titleText {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
88
src/components/molecules/PoolTransactions/Title.tsx
Normal file
88
src/components/molecules/PoolTransactions/Title.tsx
Normal file
@ -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
|
||||||
|
}
|
@ -1,7 +1,3 @@
|
|||||||
.time {
|
.time {
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleText {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
232
src/components/molecules/PoolTransactions/index.tsx
Normal file
232
src/components/molecules/PoolTransactions/index.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
@ -1,17 +1,72 @@
|
|||||||
.form {
|
.search {
|
||||||
margin-bottom: var(--spacer);
|
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%;
|
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;
|
margin: 0;
|
||||||
|
outline: 0;
|
||||||
|
padding-right: var(--spacer);
|
||||||
|
width: 0;
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form label {
|
.input:focus {
|
||||||
display: none;
|
width: calc(100% - var(--spacer));
|
||||||
}
|
|
||||||
|
|
||||||
.form input {
|
|
||||||
background-color: var(--background-content);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -10,5 +10,3 @@ export default {
|
|||||||
export const Normal = () => <SearchBar />
|
export const Normal = () => <SearchBar />
|
||||||
|
|
||||||
export const WithInitialValue = () => <SearchBar initialValue="Water" />
|
export const WithInitialValue = () => <SearchBar initialValue="Water" />
|
||||||
|
|
||||||
export const WithFilters = () => <SearchBar filters />
|
|
||||||
|
@ -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 { navigate } from 'gatsby'
|
||||||
import styles from './SearchBar.module.css'
|
import queryString from 'query-string'
|
||||||
import Button from '../atoms/Button'
|
|
||||||
import Input from '../atoms/Input'
|
|
||||||
import InputGroup from '../atoms/Input/InputGroup'
|
|
||||||
import { addExistingParamsToUrl } from '../templates/Search/utils'
|
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({
|
export default function SearchBar({
|
||||||
placeholder,
|
placeholder,
|
||||||
initialValue,
|
initialValue
|
||||||
filters,
|
|
||||||
size
|
|
||||||
}: {
|
}: {
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
initialValue?: string
|
initialValue?: string
|
||||||
filters?: boolean
|
|
||||||
size?: 'small' | 'large'
|
|
||||||
}): ReactElement {
|
}): 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>) {
|
async function startSearch(e: FormEvent<HTMLButtonElement>) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (value === '') value = ' '
|
|
||||||
|
if (value === '') setValue(' ')
|
||||||
|
|
||||||
const urlEncodedValue = encodeURIComponent(value)
|
const urlEncodedValue = encodeURIComponent(value)
|
||||||
const url = await addExistingParamsToUrl(location, [
|
const url = await addExistingParamsToUrl(location, [
|
||||||
'text',
|
'text',
|
||||||
@ -31,46 +55,38 @@ export default function SearchBar({
|
|||||||
navigate(`${url}&text=${urlEncodedValue}`)
|
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>) {
|
function handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||||
setValue(e.target.value)
|
setValue(e.target.value)
|
||||||
e.target.value === '' && emptySearch()
|
e.target.value === '' && emptySearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
async function handleKeyPress(e: KeyboardEvent<HTMLInputElement>) {
|
||||||
<form className={styles.form}>
|
if (e.key === 'Enter') {
|
||||||
<InputGroup>
|
await startSearch(e)
|
||||||
<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>
|
|
||||||
|
|
||||||
{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>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,23 +1,21 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
import FormHelp from '../../atoms/Input/Help'
|
import Input from '../../atoms/Input'
|
||||||
import InputElement from '../../atoms/Input/InputElement'
|
|
||||||
|
|
||||||
export default function Debug(): ReactElement {
|
export default function Debug(): ReactElement {
|
||||||
const { debug, setDebug } = useUserPreferences()
|
const { debug, setDebug } = useUserPreferences()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<InputElement
|
<Input
|
||||||
|
label="Debug"
|
||||||
|
help="Show geeky information in some places, and in your console."
|
||||||
name="debug"
|
name="debug"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
options={['Debug Mode']}
|
options={['Activate Debug Mode']}
|
||||||
defaultChecked={debug === true}
|
defaultChecked={debug === true}
|
||||||
onChange={() => setDebug(!debug)}
|
onChange={() => setDebug(!debug)}
|
||||||
/>
|
/>
|
||||||
<FormHelp>
|
|
||||||
Show geeky information in some places, and in your console.
|
|
||||||
</FormHelp>
|
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
76
src/components/molecules/UserPreferences/Networks/index.tsx
Normal file
76
src/components/molecules/UserPreferences/Networks/index.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
.preferences {
|
.preferences {
|
||||||
padding: calc(var(--spacer) / 5) calc(var(--spacer) / 4)
|
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3);
|
||||||
calc(var(--spacer) / 5) 0;
|
|
||||||
cursor: pointer;
|
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 {
|
.preferences svg {
|
||||||
@ -13,19 +17,28 @@
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferences svg:last-child {
|
.caret,
|
||||||
width: 1em;
|
svg.caret {
|
||||||
height: 1em;
|
width: var(--font-size-small);
|
||||||
|
height: var(--font-size-small);
|
||||||
fill: var(--border-color);
|
fill: var(--border-color);
|
||||||
margin-left: calc(var(--spacer) / 4);
|
margin-left: calc(var(--spacer) / 4);
|
||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 42rem) {
|
||||||
|
.caret,
|
||||||
|
svg.caret {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
fill: var(--brand-grey-light);
|
fill: var(--brand-grey-light);
|
||||||
transition: 0.2s ease-out;
|
transition: 0.2s ease-out;
|
||||||
width: 1.2em;
|
width: 1em;
|
||||||
height: 1.2em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferences:hover .icon,
|
.preferences:hover .icon,
|
||||||
@ -40,16 +53,16 @@
|
|||||||
max-width: 20rem;
|
max-width: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferencesDetails li > div,
|
.preferencesDetails > li > div,
|
||||||
.preferencesDetails li p {
|
.preferencesDetails p:last-child {
|
||||||
margin: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferencesDetails li p {
|
.preferencesDetails li p {
|
||||||
margin-top: calc(var(--spacer) / 8);
|
margin-top: calc(var(--spacer) / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferencesDetails li {
|
.preferencesDetails > li {
|
||||||
padding-top: calc(var(--spacer) / 3);
|
padding-top: calc(var(--spacer) / 3);
|
||||||
padding-bottom: calc(var(--spacer) / 3);
|
padding-bottom: calc(var(--spacer) / 3);
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,9 @@ import { ReactComponent as Caret } from '../../../images/caret.svg'
|
|||||||
import useDarkMode from 'use-dark-mode'
|
import useDarkMode from 'use-dark-mode'
|
||||||
import Appearance from './Appearance'
|
import Appearance from './Appearance'
|
||||||
import { darkModeConfig } from '../../../../app.config'
|
import { darkModeConfig } from '../../../../app.config'
|
||||||
import Chain from './Chain'
|
|
||||||
|
|
||||||
export default function UserPreferences(): ReactElement {
|
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)
|
const darkMode = useDarkMode(false, darkModeConfig)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -20,7 +19,6 @@ export default function UserPreferences(): ReactElement {
|
|||||||
<ul className={styles.preferencesDetails}>
|
<ul className={styles.preferencesDetails}>
|
||||||
<Currency />
|
<Currency />
|
||||||
<Appearance darkMode={darkMode} />
|
<Appearance darkMode={darkMode} />
|
||||||
<Chain />
|
|
||||||
<Debug />
|
<Debug />
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
@ -28,7 +26,7 @@ export default function UserPreferences(): ReactElement {
|
|||||||
className={styles.preferences}
|
className={styles.preferences}
|
||||||
>
|
>
|
||||||
<Cog aria-label="Preferences" className={styles.icon} />
|
<Cog aria-label="Preferences" className={styles.icon} />
|
||||||
<Caret aria-hidden="true" />
|
<Caret aria-hidden="true" className={styles.caret} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,15 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
padding: calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background: var(--background-content);
|
background: var(--background-content);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
transition: border 0.2s ease-out;
|
transition: border 0.2s ease-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 190px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button,
|
.button,
|
||||||
@ -30,6 +31,16 @@
|
|||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.initial span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 42rem) {
|
||||||
|
.button.initial span {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.blockies {
|
.blockies {
|
||||||
width: var(--font-size-large);
|
width: var(--font-size-large);
|
||||||
height: var(--font-size-large);
|
height: var(--font-size-large);
|
||||||
@ -37,15 +48,25 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: calc(var(--spacer) / 6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.address {
|
.address {
|
||||||
|
display: none;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
border-right: 1px solid var(--border-color);
|
border-right: 1px solid var(--border-color);
|
||||||
padding-right: calc(var(--spacer) / 3);
|
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 {
|
.button svg {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@ -65,3 +86,15 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.caret,
|
||||||
|
svg.caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 42rem) {
|
||||||
|
.caret,
|
||||||
|
svg.caret {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,7 +35,7 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
return !accountId && web3Modal?.cachedProvider ? (
|
return !accountId && web3Modal?.cachedProvider ? (
|
||||||
// Improve user experience for cached provider when connecting takes some time
|
// Improve user experience for cached provider when connecting takes some time
|
||||||
<button className={styles.button} onClick={(e) => e.preventDefault()}>
|
<button className={styles.button} onClick={(e) => e.preventDefault()}>
|
||||||
<Loader message="Reconnecting wallet..." />
|
<Loader message="Reconnecting..." />
|
||||||
</button>
|
</button>
|
||||||
) : accountId ? (
|
) : accountId ? (
|
||||||
<button
|
<button
|
||||||
@ -48,7 +48,7 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
<span className={styles.address} title={accountId}>
|
<span className={styles.address} title={accountId}>
|
||||||
{accountTruncate(accountId)}
|
{accountTruncate(accountId)}
|
||||||
</span>
|
</span>
|
||||||
<Caret aria-hidden="true" />
|
<Caret aria-hidden="true" className={styles.caret} />
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
@ -58,7 +58,7 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
// the Tippy to show in this state.
|
// the Tippy to show in this state.
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
Connect Wallet
|
Connect <span>Wallet</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { formatCurrency } from '@coingecko/cryptoformat'
|
import { formatCurrency } from '@coingecko/cryptoformat'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
import AddToken from '../../atoms/AddToken'
|
import AddToken from '../../atoms/AddToken'
|
||||||
import Conversion from '../../atoms/Price/Conversion'
|
import Conversion from '../../atoms/Price/Conversion'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
|
|
||||||
import Web3Feedback from './Feedback'
|
import Web3Feedback from '../Web3Feedback'
|
||||||
import styles from './Details.module.css'
|
import styles from './Details.module.css'
|
||||||
|
import { getOceanConfig } from '../../../utils/ocean'
|
||||||
|
|
||||||
export default function Details(): ReactElement {
|
export default function Details(): ReactElement {
|
||||||
const {
|
const {
|
||||||
@ -17,20 +17,34 @@ export default function Details(): ReactElement {
|
|||||||
web3Modal,
|
web3Modal,
|
||||||
connect,
|
connect,
|
||||||
logout,
|
logout,
|
||||||
|
networkData,
|
||||||
networkId,
|
networkId,
|
||||||
networkData
|
balance
|
||||||
} = useWeb3()
|
} = useWeb3()
|
||||||
const { balance, config } = useOcean()
|
|
||||||
const { locale } = useUserPreferences()
|
const { locale } = useUserPreferences()
|
||||||
|
|
||||||
const [mainCurrency, setMainCurrency] = useState<string>()
|
const [mainCurrency, setMainCurrency] = useState<string>()
|
||||||
|
const [oceanTokenMetadata, setOceanTokenMetadata] =
|
||||||
|
useState<{
|
||||||
|
address: string
|
||||||
|
symbol: string
|
||||||
|
}>()
|
||||||
// const [portisNetwork, setPortisNetwork] = useState<string>()
|
// const [portisNetwork, setPortisNetwork] = useState<string>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!networkId) return
|
||||||
|
|
||||||
const symbol =
|
const symbol =
|
||||||
networkId === 2021000 ? 'GX' : networkData?.nativeCurrency.symbol
|
networkId === 2021000 ? 'GX' : networkData?.nativeCurrency.symbol
|
||||||
|
|
||||||
setMainCurrency(symbol)
|
setMainCurrency(symbol)
|
||||||
|
|
||||||
|
const oceanConfig = getOceanConfig(networkId)
|
||||||
|
|
||||||
|
oceanConfig &&
|
||||||
|
setOceanTokenMetadata({
|
||||||
|
address: oceanConfig.oceanTokenAddress,
|
||||||
|
symbol: oceanConfig.oceanTokenSymbol
|
||||||
|
})
|
||||||
}, [networkData, networkId])
|
}, [networkData, networkId])
|
||||||
|
|
||||||
// Handle network change for Portis
|
// Handle network change for Portis
|
||||||
@ -49,7 +63,7 @@ export default function Details(): ReactElement {
|
|||||||
{Object.entries(balance).map(([key, value]) => (
|
{Object.entries(balance).map(([key, value]) => (
|
||||||
<li className={styles.balance} key={key}>
|
<li className={styles.balance} key={key}>
|
||||||
<span className={styles.symbol}>
|
<span className={styles.symbol}>
|
||||||
{key === 'eth' ? mainCurrency : config.oceanTokenSymbol}
|
{key === 'eth' ? mainCurrency : oceanTokenMetadata?.symbol}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{formatCurrency(Number(value), '', locale, false, {
|
{formatCurrency(Number(value), '', locale, false, {
|
||||||
significantFigures: 4
|
significantFigures: 4
|
||||||
@ -76,8 +90,8 @@ export default function Details(): ReactElement {
|
|||||||
)} */}
|
)} */}
|
||||||
{web3ProviderInfo?.name === 'MetaMask' && (
|
{web3ProviderInfo?.name === 'MetaMask' && (
|
||||||
<AddToken
|
<AddToken
|
||||||
address={config.oceanTokenAddress}
|
address={oceanTokenMetadata?.address}
|
||||||
symbol={config.oceanTokenSymbol}
|
symbol={oceanTokenMetadata?.symbol}
|
||||||
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png"
|
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png"
|
||||||
className={styles.addToken}
|
className={styles.addToken}
|
||||||
/>
|
/>
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -3,7 +3,7 @@
|
|||||||
border-right: none;
|
border-right: none;
|
||||||
border-top-left-radius: var(--border-radius);
|
border-top-left-radius: var(--border-radius);
|
||||||
border-bottom-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;
|
white-space: nowrap;
|
||||||
margin-right: -3px;
|
margin-right: -3px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -12,8 +12,6 @@
|
|||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
display: inline-block;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
@ -1,41 +1,36 @@
|
|||||||
import React, { useState, useEffect, ReactElement } from 'react'
|
import React, { useState, useEffect, ReactElement } from 'react'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
|
||||||
import Status from '../../atoms/Status'
|
import Status from '../../atoms/Status'
|
||||||
import { ConfigHelper, ConfigHelperConfig } from '@oceanprotocol/lib'
|
|
||||||
import styles from './Network.module.css'
|
|
||||||
import Badge from '../../atoms/Badge'
|
import Badge from '../../atoms/Badge'
|
||||||
import Tooltip from '../../atoms/Tooltip'
|
import Tooltip from '../../atoms/Tooltip'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
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 {
|
export default function Network(): ReactElement {
|
||||||
const { networkId, networkDisplayName, isTestnet } = useWeb3()
|
const { networkId, isTestnet } = useWeb3()
|
||||||
const { config } = useOcean()
|
|
||||||
const networkIdConfig = (config as ConfigHelperConfig).networkId
|
|
||||||
|
|
||||||
const [isEthMainnet, setIsEthMainnet] = useState<boolean>()
|
const [isSupportedOceanNetwork, setIsSupportedOceanNetwork] =
|
||||||
const [isSupportedNetwork, setIsSupportedNetwork] = useState<boolean>()
|
useState<boolean>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// take network from user when present,
|
// take network from user when present
|
||||||
// otherwise use the default configured one of app
|
const network = networkId || 1
|
||||||
const network = networkId || networkIdConfig
|
|
||||||
const isEthMainnet = network === 1
|
|
||||||
setIsEthMainnet(isEthMainnet)
|
|
||||||
|
|
||||||
// Check networkId against ocean.js ConfigHelper configs
|
// Check networkId against ocean.js ConfigHelper configs
|
||||||
// to figure out if network is supported.
|
// to figure out if network is supported.
|
||||||
const isSupportedNetwork = Boolean(new ConfigHelper().getConfig(network))
|
const isSupportedOceanNetwork = Boolean(getOceanConfig(network))
|
||||||
setIsSupportedNetwork(isSupportedNetwork)
|
setIsSupportedOceanNetwork(isSupportedOceanNetwork)
|
||||||
}, [networkId, networkIdConfig])
|
}, [networkId])
|
||||||
|
|
||||||
return !isEthMainnet && networkDisplayName ? (
|
return networkId ? (
|
||||||
<div className={styles.network}>
|
<div className={styles.network}>
|
||||||
{!isSupportedNetwork && (
|
{!isSupportedOceanNetwork && (
|
||||||
<Tooltip content="No Ocean Protocol contracts are deployed to this network.">
|
<Tooltip content="No Ocean Protocol contracts are deployed to this network.">
|
||||||
<Status state="error" className={styles.warning} />
|
<Status state="error" className={styles.warning} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<span className={styles.name}>{networkDisplayName}</span>
|
<NetworkName className={styles.name} networkId={networkId} minimal />
|
||||||
{isTestnet && <Badge label="Test" className={styles.badge} />}
|
{isTestnet && <Badge label="Test" className={styles.badge} />}
|
||||||
</div>
|
</div>
|
||||||
) : null
|
) : null
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
.wallet {
|
.wallet {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Web3Feedback from './Feedback'
|
import Web3Feedback from '../Web3Feedback'
|
||||||
import web3Mock from '../../../../tests/unit/__mocks__/web3'
|
import web3Mock from '../../../../tests/unit/__mocks__/web3'
|
||||||
import { Center } from '../../../../.storybook/helpers'
|
import { Center } from '../../../../.storybook/helpers'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Account from './Account'
|
import Account from './Account'
|
||||||
import Details from './Details'
|
import Details from './Details'
|
||||||
import Tooltip from '../../atoms/Tooltip'
|
import Tooltip from '../../atoms/Tooltip'
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
.text {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-top: calc(var(--spacer) / 4);
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
51
src/components/molecules/WalletNetworkSwitcher.tsx
Normal file
51
src/components/molecules/WalletNetworkSwitcher.tsx
Normal file
@ -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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -2,8 +2,7 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
padding-left: var(--spacer);
|
padding-left: var(--spacer);
|
||||||
padding-top: calc(var(--spacer) / 1.5);
|
padding-top: calc(var(--spacer) / 1.5);
|
||||||
margin-top: var(--spacer);
|
margin-top: calc(var(--spacer) / 2);
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@ -11,7 +10,7 @@
|
|||||||
.feedback i {
|
.feedback i {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc(var(--spacer) / 1.5);
|
top: calc(var(--spacer) / 1.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
70
src/components/molecules/Web3Feedback.tsx
Normal file
70
src/components/molecules/Web3Feedback.tsx
Normal file
@ -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
|
||||||
|
}
|
@ -94,7 +94,7 @@ export default function FormStartCompute({
|
|||||||
|
|
||||||
const { isValid, values }: FormikContextType<{ algorithm: string }> =
|
const { isValid, values }: FormikContextType<{ algorithm: string }> =
|
||||||
useFormikContext()
|
useFormikContext()
|
||||||
const { price, ddo } = useAsset()
|
const { price, ddo, isAssetNetwork } = useAsset()
|
||||||
const [totalPrice, setTotalPrice] = useState(price?.value)
|
const [totalPrice, setTotalPrice] = useState(price?.value)
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
@ -175,7 +175,10 @@ export default function FormStartCompute({
|
|||||||
<ButtonBuy
|
<ButtonBuy
|
||||||
action="compute"
|
action="compute"
|
||||||
disabled={
|
disabled={
|
||||||
isComputeButtonDisabled || !isValid || algorithmConsumableStatus > 0
|
isComputeButtonDisabled ||
|
||||||
|
!isValid ||
|
||||||
|
!isAssetNetwork ||
|
||||||
|
algorithmConsumableStatus > 0
|
||||||
}
|
}
|
||||||
hasPreviousOrder={hasPreviousOrder}
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
|
@ -10,7 +10,6 @@ import { toast } from 'react-toastify'
|
|||||||
import Price from '../../../atoms/Price'
|
import Price from '../../../atoms/Price'
|
||||||
import File from '../../../atoms/File'
|
import File from '../../../atoms/File'
|
||||||
import Alert from '../../../atoms/Alert'
|
import Alert from '../../../atoms/Alert'
|
||||||
import Web3Feedback from '../../../molecules/Wallet/Feedback'
|
|
||||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
@ -39,6 +38,7 @@ import { secondsToString } from '../../../../utils/metadata'
|
|||||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||||
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
||||||
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
||||||
|
import { chainIds } from '../../../../../app.config'
|
||||||
|
|
||||||
const SuccessAction = () => (
|
const SuccessAction = () => (
|
||||||
<Button style="text" to="/history?defaultTab=ComputeJobs" size="small">
|
<Button style="text" to="/history?defaultTab=ComputeJobs" size="small">
|
||||||
@ -63,8 +63,8 @@ export default function Compute({
|
|||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean, account, config } = useOcean()
|
const { ocean, account } = useOcean()
|
||||||
const { price, type, ddo } = useAsset()
|
const { price, type, ddo, isAssetNetwork } = useAsset()
|
||||||
const { buyDT, pricingError, pricingStepText } = usePricing()
|
const { buyDT, pricingError, pricingStepText } = usePricing()
|
||||||
const [isJobStarting, setIsJobStarting] = useState(false)
|
const [isJobStarting, setIsJobStarting] = useState(false)
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
@ -142,7 +142,8 @@ export default function Compute({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getQuerryString(
|
function getQuerryString(
|
||||||
trustedAlgorithmList: publisherTrustedAlgorithm[]
|
trustedAlgorithmList: publisherTrustedAlgorithm[],
|
||||||
|
chainId?: number
|
||||||
): SearchQuery {
|
): SearchQuery {
|
||||||
let algoQuerry = ''
|
let algoQuerry = ''
|
||||||
trustedAlgorithmList.forEach((trusteAlgo) => {
|
trustedAlgorithmList.forEach((trusteAlgo) => {
|
||||||
@ -157,7 +158,7 @@ export default function Compute({
|
|||||||
offset: 500,
|
offset: 500,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
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 }
|
sort: { created: -1 }
|
||||||
@ -180,9 +181,9 @@ export default function Compute({
|
|||||||
} else {
|
} else {
|
||||||
const gueryResults = await queryMetadata(
|
const gueryResults = await queryMetadata(
|
||||||
getQuerryString(
|
getQuerryString(
|
||||||
computeService.attributes.main.privacy.publisherTrustedAlgorithms
|
computeService.attributes.main.privacy.publisherTrustedAlgorithms,
|
||||||
|
ddo.chainId
|
||||||
),
|
),
|
||||||
config.metadataCacheUri,
|
|
||||||
source.token
|
source.token
|
||||||
)
|
)
|
||||||
setDdoAlgorithmList(gueryResults.results)
|
setDdoAlgorithmList(gueryResults.results)
|
||||||
@ -190,7 +191,6 @@ export default function Compute({
|
|||||||
algorithmSelectionList = await transformDDOToAssetSelection(
|
algorithmSelectionList = await transformDDOToAssetSelection(
|
||||||
datasetComputeService?.serviceEndpoint,
|
datasetComputeService?.serviceEndpoint,
|
||||||
gueryResults.results,
|
gueryResults.results,
|
||||||
config.metadataCacheUri,
|
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -472,9 +472,6 @@ export default function Compute({
|
|||||||
action={<SuccessAction />}
|
action={<SuccessAction />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{type !== 'algorithm' && (
|
|
||||||
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
|
||||||
)}
|
|
||||||
</footer>
|
</footer>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
.info {
|
.info {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0 calc(var(--spacer)) 0 calc(var(--spacer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filewrapper {
|
.filewrapper {
|
||||||
@ -15,4 +14,5 @@
|
|||||||
|
|
||||||
.feedback {
|
.feedback {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-top: calc(var(--spacer));
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,9 @@ import { toast } from 'react-toastify'
|
|||||||
import { File as FileMetadata, DDO, BestPrice } from '@oceanprotocol/lib'
|
import { File as FileMetadata, DDO, BestPrice } from '@oceanprotocol/lib'
|
||||||
import File from '../../atoms/File'
|
import File from '../../atoms/File'
|
||||||
import Price from '../../atoms/Price'
|
import Price from '../../atoms/Price'
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
|
||||||
import styles from './Consume.module.css'
|
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
import { useAsset } from '../../../providers/Asset'
|
import { useAsset } from '../../../providers/Asset'
|
||||||
import { secondsToString } from '../../../utils/metadata'
|
import { gql, useQuery } from 'urql'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
|
||||||
import { OrdersData } from '../../../@types/apollo/OrdersData'
|
import { OrdersData } from '../../../@types/apollo/OrdersData'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
import { useOcean } from '../../../providers/Ocean'
|
||||||
@ -16,7 +13,9 @@ import { useWeb3 } from '../../../providers/Web3'
|
|||||||
import { usePricing } from '../../../hooks/usePricing'
|
import { usePricing } from '../../../hooks/usePricing'
|
||||||
import { useConsume } from '../../../hooks/useConsume'
|
import { useConsume } from '../../../hooks/useConsume'
|
||||||
import ButtonBuy from '../../atoms/ButtonBuy'
|
import ButtonBuy from '../../atoms/ButtonBuy'
|
||||||
|
import { secondsToString } from '../../../utils/metadata'
|
||||||
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute'
|
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute'
|
||||||
|
import styles from './Consume.module.css'
|
||||||
|
|
||||||
const previousOrderQuery = gql`
|
const previousOrderQuery = gql`
|
||||||
query PreviousOrder($id: String!, $account: String!) {
|
query PreviousOrder($id: String!, $account: String!) {
|
||||||
@ -54,7 +53,7 @@ export default function Consume({
|
|||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
|
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
|
||||||
const [previousOrderId, setPreviousOrderId] = useState<string>()
|
const [previousOrderId, setPreviousOrderId] = useState<string>()
|
||||||
const { isInPurgatory, price, type } = useAsset()
|
const { isInPurgatory, price, type, isAssetNetwork } = useAsset()
|
||||||
const { buyDT, pricingStepText, pricingError, pricingIsLoading } =
|
const { buyDT, pricingStepText, pricingError, pricingIsLoading } =
|
||||||
usePricing()
|
usePricing()
|
||||||
const { consumeStepText, consume, consumeError, isLoading } = useConsume()
|
const { consumeStepText, consume, consumeError, isLoading } = useConsume()
|
||||||
@ -63,13 +62,15 @@ export default function Consume({
|
|||||||
const [maxDt, setMaxDT] = useState<number>(1)
|
const [maxDt, setMaxDT] = useState<number>(1)
|
||||||
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
|
const [isConsumablePrice, setIsConsumablePrice] = useState(true)
|
||||||
const [assetTimeout, setAssetTimeout] = useState('')
|
const [assetTimeout, setAssetTimeout] = useState('')
|
||||||
const { data } = useQuery<OrdersData>(previousOrderQuery, {
|
const [result] = useQuery<OrdersData>({
|
||||||
|
query: previousOrderQuery,
|
||||||
variables: {
|
variables: {
|
||||||
id: ddo.dataToken?.toLowerCase(),
|
id: ddo.dataToken?.toLowerCase(),
|
||||||
account: accountId?.toLowerCase()
|
account: accountId?.toLowerCase()
|
||||||
},
|
}
|
||||||
pollInterval: 5000
|
// pollInterval: 5000
|
||||||
})
|
})
|
||||||
|
const { data } = result
|
||||||
|
|
||||||
async function checkMaxAvaialableTokens(price: BestPrice) {
|
async function checkMaxAvaialableTokens(price: BestPrice) {
|
||||||
if (!ocean || !price) return
|
if (!ocean || !price) return
|
||||||
@ -122,6 +123,7 @@ export default function Consume({
|
|||||||
!isConsumable ||
|
!isConsumable ||
|
||||||
((!ocean ||
|
((!ocean ||
|
||||||
!isBalanceSufficient ||
|
!isBalanceSufficient ||
|
||||||
|
!isAssetNetwork ||
|
||||||
typeof consumeStepText !== 'undefined' ||
|
typeof consumeStepText !== 'undefined' ||
|
||||||
pricingIsLoading ||
|
pricingIsLoading ||
|
||||||
(!hasPreviousOrder && !hasDatatoken && !(maxDt >= 1)) ||
|
(!hasPreviousOrder && !hasDatatoken && !(maxDt >= 1)) ||
|
||||||
@ -133,6 +135,7 @@ export default function Consume({
|
|||||||
ocean,
|
ocean,
|
||||||
hasPreviousOrder,
|
hasPreviousOrder,
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
|
isAssetNetwork,
|
||||||
consumeStepText,
|
consumeStepText,
|
||||||
pricingIsLoading,
|
pricingIsLoading,
|
||||||
isConsumablePrice,
|
isConsumablePrice,
|
||||||
@ -198,9 +201,6 @@ export default function Consume({
|
|||||||
{type === 'algorithm' && (
|
{type === 'algorithm' && (
|
||||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} dataset={ddo} />
|
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} dataset={ddo} />
|
||||||
)}
|
)}
|
||||||
<footer className={styles.feedback}>
|
|
||||||
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
|
||||||
</footer>
|
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
setMinterToDispenser,
|
setMinterToDispenser,
|
||||||
setMinterToPublisher
|
setMinterToPublisher
|
||||||
} from '../../../../utils/freePrice'
|
} from '../../../../utils/freePrice'
|
||||||
|
import Web3Feedback from '../../../molecules/Web3Feedback'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query EditAvanceSettingsQuery {
|
query EditAvanceSettingsQuery {
|
||||||
@ -72,7 +73,7 @@ export default function EditAdvancedSettings({
|
|||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { metadata, ddo, refreshDdo, price } = useAsset()
|
const { isAssetNetwork, ddo, refreshDdo, price } = useAsset()
|
||||||
const [success, setSuccess] = useState<string>()
|
const [success, setSuccess] = useState<string>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
@ -168,7 +169,7 @@ export default function EditAdvancedSettings({
|
|||||||
setShowEdit={setShowEdit}
|
setShowEdit={setShowEdit}
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
|
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||||
{debug === true && (
|
{debug === true && (
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<DebugEditCredential
|
<DebugEditCredential
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
setMinterToDispenser,
|
setMinterToDispenser,
|
||||||
setMinterToPublisher
|
setMinterToPublisher
|
||||||
} from '../../../../utils/freePrice'
|
} from '../../../../utils/freePrice'
|
||||||
|
import Web3Feedback from '../../../molecules/Web3Feedback'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query EditComputeDataQuery {
|
query EditComputeDataQuery {
|
||||||
@ -66,7 +67,7 @@ export default function EditComputeDataset({
|
|||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ddo, refreshDdo, price } = useAsset()
|
const { ddo, price, isAssetNetwork, refreshDdo } = useAsset()
|
||||||
const [success, setSuccess] = useState<string>()
|
const [success, setSuccess] = useState<string>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
|
||||||
@ -169,7 +170,7 @@ export default function EditComputeDataset({
|
|||||||
setShowEdit={setShowEdit}
|
setShowEdit={setShowEdit}
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
|
<Web3Feedback isAssetNetwork={isAssetNetwork} />
|
||||||
{debug === true && (
|
{debug === true && (
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<DebugEditCompute values={values} ddo={ddo} />
|
<DebugEditCompute values={values} ddo={ddo} />
|
||||||
|
@ -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);
|
||||||
|
}
|
37
src/components/organisms/AssetActions/Edit/FormActions.tsx
Normal file
37
src/components/organisms/AssetActions/Edit/FormActions.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
import React, { ChangeEvent, ReactElement } from 'react'
|
import React, { ChangeEvent, ReactElement } from 'react'
|
||||||
import styles from './FormEditMetadata.module.css'
|
import styles from './FormEditMetadata.module.css'
|
||||||
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
||||||
import Button from '../../../atoms/Button'
|
|
||||||
import Input from '../../../atoms/Input'
|
import Input from '../../../atoms/Input'
|
||||||
import { FormFieldProps } from '../../../../@types/Form'
|
import { FormFieldProps } from '../../../../@types/Form'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
|
||||||
import { AdvancedSettingsForm } from '../../../../models/FormEditCredential'
|
import { AdvancedSettingsForm } from '../../../../models/FormEditCredential'
|
||||||
|
import FormActions from './FormActions'
|
||||||
|
|
||||||
export default function FormAdvancedSettings({
|
export default function FormAdvancedSettings({
|
||||||
data,
|
data,
|
||||||
@ -15,10 +13,7 @@ export default function FormAdvancedSettings({
|
|||||||
data: FormFieldProps[]
|
data: FormFieldProps[]
|
||||||
setShowEdit: (show: boolean) => void
|
setShowEdit: (show: boolean) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { ocean, config } = useOcean()
|
|
||||||
const {
|
const {
|
||||||
isValid,
|
|
||||||
validateField,
|
validateField,
|
||||||
setFieldValue
|
setFieldValue
|
||||||
}: FormikContextType<Partial<AdvancedSettingsForm>> = useFormikContext()
|
}: FormikContextType<Partial<AdvancedSettingsForm>> = useFormikContext()
|
||||||
@ -46,14 +41,7 @@ export default function FormAdvancedSettings({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<footer className={styles.actions}>
|
<FormActions setShowEdit={setShowEdit} />
|
||||||
<Button style="primary" disabled={!ocean || !accountId || !isValid}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</footer>
|
|
||||||
</Form>
|
</Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
||||||
import Button from '../../../atoms/Button'
|
|
||||||
import Input from '../../../atoms/Input'
|
import Input from '../../../atoms/Input'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
|
||||||
import { FormFieldProps } from '../../../../@types/Form'
|
import { FormFieldProps } from '../../../../@types/Form'
|
||||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
@ -16,6 +13,8 @@ import { useAsset } from '../../../../providers/Asset'
|
|||||||
import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
|
import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
|
||||||
import { publisherTrustedAlgorithm as PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
|
import { publisherTrustedAlgorithm as PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
import FormActions from './FormActions'
|
||||||
|
|
||||||
export default function FormEditComputeDataset({
|
export default function FormEditComputeDataset({
|
||||||
data,
|
data,
|
||||||
@ -26,11 +25,9 @@ export default function FormEditComputeDataset({
|
|||||||
title: string
|
title: string
|
||||||
setShowEdit: (show: boolean) => void
|
setShowEdit: (show: boolean) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const { appConfig } = useSiteMetadata()
|
||||||
const { ocean, config } = useOcean()
|
|
||||||
const { ddo } = useAsset()
|
const { ddo } = useAsset()
|
||||||
const { isValid, values }: FormikContextType<ComputePrivacyForm> =
|
const { values }: FormikContextType<ComputePrivacyForm> = useFormikContext()
|
||||||
useFormikContext()
|
|
||||||
const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>()
|
const [allAlgorithms, setAllAlgorithms] = useState<AssetSelectionAsset[]>()
|
||||||
|
|
||||||
const { publisherTrustedAlgorithms } =
|
const { publisherTrustedAlgorithms } =
|
||||||
@ -44,21 +41,16 @@ export default function FormEditComputeDataset({
|
|||||||
offset: 500,
|
offset: 500,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
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 }
|
sort: { created: -1 }
|
||||||
}
|
}
|
||||||
const querryResult = await queryMetadata(
|
const querryResult = await queryMetadata(query, source.token)
|
||||||
query,
|
|
||||||
config.metadataCacheUri,
|
|
||||||
source.token
|
|
||||||
)
|
|
||||||
const datasetComputeService = ddo.findServiceByType('compute')
|
const datasetComputeService = ddo.findServiceByType('compute')
|
||||||
const algorithmSelectionList = await transformDDOToAssetSelection(
|
const algorithmSelectionList = await transformDDOToAssetSelection(
|
||||||
datasetComputeService?.serviceEndpoint,
|
datasetComputeService?.serviceEndpoint,
|
||||||
querryResult.results,
|
querryResult.results,
|
||||||
config.metadataCacheUri,
|
|
||||||
publisherTrustedAlgorithms
|
publisherTrustedAlgorithms
|
||||||
)
|
)
|
||||||
return algorithmSelectionList
|
return algorithmSelectionList
|
||||||
@ -68,7 +60,7 @@ export default function FormEditComputeDataset({
|
|||||||
getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => {
|
getAlgorithmList(publisherTrustedAlgorithms).then((algorithms) => {
|
||||||
setAllAlgorithms(algorithms)
|
setAllAlgorithms(algorithms)
|
||||||
})
|
})
|
||||||
}, [config.metadataCacheUri, publisherTrustedAlgorithms])
|
}, [appConfig.metadataCacheUri, publisherTrustedAlgorithms])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className={styles.form}>
|
<Form className={styles.form}>
|
||||||
@ -90,14 +82,8 @@ export default function FormEditComputeDataset({
|
|||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<footer className={styles.actions}>
|
|
||||||
<Button style="primary" disabled={!ocean || !accountId || !isValid}>
|
<FormActions setShowEdit={setShowEdit} />
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</footer>
|
|
||||||
</Form>
|
</Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,27 +2,6 @@
|
|||||||
composes: box from '../../../atoms/Box.module.css';
|
composes: box from '../../../atoms/Box.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.form select[multiple] {
|
||||||
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] {
|
|
||||||
height: 130px;
|
height: 130px;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import React, { ChangeEvent, ReactElement } from 'react'
|
import React, { ChangeEvent, ReactElement } from 'react'
|
||||||
import styles from './FormEditMetadata.module.css'
|
|
||||||
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
||||||
import Button from '../../../atoms/Button'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import Input from '../../../atoms/Input'
|
import Input from '../../../atoms/Input'
|
||||||
import { FormFieldProps } from '../../../../@types/Form'
|
import { FormFieldProps } from '../../../../@types/Form'
|
||||||
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
||||||
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
|
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import FormActions from './FormActions'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import styles from './FormEditMetadata.module.css'
|
||||||
|
|
||||||
function handleTimeoutCustomOption(
|
function handleTimeoutCustomOption(
|
||||||
data: FormFieldProps[],
|
data: FormFieldProps[],
|
||||||
@ -56,10 +55,8 @@ export default function FormEditMetadata({
|
|||||||
values: Partial<MetadataPublishFormDataset>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
showPrice: boolean
|
showPrice: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const { config } = useOcean()
|
||||||
const { ocean, config } = useOcean()
|
|
||||||
const {
|
const {
|
||||||
isValid,
|
|
||||||
validateField,
|
validateField,
|
||||||
setFieldValue
|
setFieldValue
|
||||||
}: FormikContextType<Partial<MetadataPublishFormDataset>> = useFormikContext()
|
}: FormikContextType<Partial<MetadataPublishFormDataset>> = useFormikContext()
|
||||||
@ -96,18 +93,10 @@ export default function FormEditMetadata({
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<footer className={styles.actions}>
|
<FormActions
|
||||||
<Button
|
setShowEdit={setShowEdit}
|
||||||
style="primary"
|
handleClick={() => setTimeoutStringValue(values.timeout)}
|
||||||
disabled={!ocean || !accountId || !isValid}
|
/>
|
||||||
onClick={() => setTimeoutStringValue(values.timeout)}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</footer>
|
|
||||||
</Form>
|
</Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import { useAsset } from '../../../../providers/Asset'
|
|||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
import { MetadataPreview } from '../../../molecules/MetadataPreview'
|
import { MetadataPreview } from '../../../molecules/MetadataPreview'
|
||||||
import Debug from './DebugEditMetadata'
|
import Debug from './DebugEditMetadata'
|
||||||
import Web3Feedback from '../../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../../molecules/Web3Feedback'
|
||||||
import FormEditMetadata from './FormEditMetadata'
|
import FormEditMetadata from './FormEditMetadata'
|
||||||
import { mapTimeoutStringToSeconds } from '../../../../utils/metadata'
|
import { mapTimeoutStringToSeconds } from '../../../../utils/metadata'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
@ -13,6 +13,7 @@ import { FormAddLiquidity } from '.'
|
|||||||
import { PoolBalance } from '../../../../../@types/TokenBalance'
|
import { PoolBalance } from '../../../../../@types/TokenBalance'
|
||||||
import UserLiquidity from '../../../../atoms/UserLiquidity'
|
import UserLiquidity from '../../../../atoms/UserLiquidity'
|
||||||
import { useOcean } from '../../../../../providers/Ocean'
|
import { useOcean } from '../../../../../providers/Ocean'
|
||||||
|
import { useWeb3 } from '../../../../../providers/Web3'
|
||||||
|
|
||||||
export default function FormAdd({
|
export default function FormAdd({
|
||||||
coin,
|
coin,
|
||||||
@ -37,7 +38,8 @@ export default function FormAdd({
|
|||||||
setNewPoolTokens: (value: string) => void
|
setNewPoolTokens: (value: string) => void
|
||||||
setNewPoolShare: (value: string) => void
|
setNewPoolShare: (value: string) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, balance } = useOcean()
|
const { balance } = useWeb3()
|
||||||
|
const { ocean } = useOcean()
|
||||||
|
|
||||||
// Connect with form
|
// Connect with form
|
||||||
const {
|
const {
|
||||||
|
@ -65,8 +65,8 @@ export default function Add({
|
|||||||
const data = useStaticQuery(contentQuery)
|
const data = useStaticQuery(contentQuery)
|
||||||
const content = data.content.edges[0].node.childContentJson.pool.add
|
const content = data.content.edges[0].node.childContentJson.pool.add
|
||||||
|
|
||||||
const { accountId } = useWeb3()
|
const { accountId, balance } = useWeb3()
|
||||||
const { ocean, balance } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const [txId, setTxId] = useState<string>()
|
const [txId, setTxId] = useState<string>()
|
||||||
const [coin, setCoin] = useState('OCEAN')
|
const [coin, setCoin] = useState('OCEAN')
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
ChartTooltipItem,
|
ChartTooltipItem,
|
||||||
ChartTooltipOptions
|
ChartTooltipOptions
|
||||||
} from 'chart.js'
|
} from 'chart.js'
|
||||||
import styles from './Graph.module.css'
|
|
||||||
import Loader from '../../../atoms/Loader'
|
import Loader from '../../../atoms/Loader'
|
||||||
import { formatPrice } from '../../../atoms/Price/PriceUnit'
|
import { formatPrice } from '../../../atoms/Price/PriceUnit'
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
@ -17,8 +16,10 @@ import { darkModeConfig } from '../../../../../app.config'
|
|||||||
import Button from '../../../atoms/Button'
|
import Button from '../../../atoms/Button'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
import { useAsset } from '../../../../providers/Asset'
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
import { gql, OperationResult } from 'urql'
|
||||||
import { PoolHistory } from '../../../../@types/apollo/PoolHistory'
|
import { PoolHistory } from '../../../../@types/apollo/PoolHistory'
|
||||||
|
import { fetchData, getQueryContext } from '../../../../utils/subgraph'
|
||||||
|
import styles from './Graph.module.css'
|
||||||
|
|
||||||
declare type GraphType = 'liquidity' | 'price'
|
declare type GraphType = 'liquidity' | 'price'
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ defaults.global.defaultFontFamily = `'Sharp Sans', -apple-system, BlinkMacSystem
|
|||||||
'Segoe UI', Helvetica, Arial, sans-serif`
|
'Segoe UI', Helvetica, Arial, sans-serif`
|
||||||
defaults.global.animation = { easing: 'easeInOutQuart', duration: 1000 }
|
defaults.global.animation = { easing: 'easeInOutQuart', duration: 1000 }
|
||||||
|
|
||||||
|
const REFETCH_INTERVAL = 10000
|
||||||
|
|
||||||
const lineStyle: Partial<ChartDataSets> = {
|
const lineStyle: Partial<ChartDataSets> = {
|
||||||
fill: false,
|
fill: false,
|
||||||
lineTension: 0.1,
|
lineTension: 0.1,
|
||||||
@ -121,22 +124,53 @@ export default function Graph(): ReactElement {
|
|||||||
const [options, setOptions] = useState<ChartOptions>()
|
const [options, setOptions] = useState<ChartOptions>()
|
||||||
const [graphType, setGraphType] = useState<GraphType>('liquidity')
|
const [graphType, setGraphType] = useState<GraphType>('liquidity')
|
||||||
|
|
||||||
const { price } = useAsset()
|
const { price, ddo } = useAsset()
|
||||||
|
|
||||||
const [lastBlock, setLastBlock] = useState<number>(0)
|
const [lastBlock, setLastBlock] = useState<number>(0)
|
||||||
const [priceHistory, setPriceHistory] = useState([])
|
const [priceHistory, setPriceHistory] = useState([])
|
||||||
|
const [error, setError] = useState<Error>()
|
||||||
const [liquidityHistory, setLiquidityHistory] = useState([])
|
const [liquidityHistory, setLiquidityHistory] = useState([])
|
||||||
const [timestamps, setTimestamps] = useState([])
|
const [timestamps, setTimestamps] = useState([])
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [dataHistory, setDataHistory] = useState<PoolHistory>()
|
||||||
const [graphData, setGraphData] = useState<ChartData>()
|
const [graphData, setGraphData] = useState<ChartData>()
|
||||||
|
const [graphFetchInterval, setGraphFetchInterval] = useState<NodeJS.Timeout>()
|
||||||
|
|
||||||
const { data, refetch, error } = useQuery<PoolHistory>(poolHistory, {
|
async function getPoolHistory() {
|
||||||
variables: {
|
try {
|
||||||
id: price.address.toLowerCase(),
|
const queryContext = getQueryContext(ddo.chainId)
|
||||||
block: lastBlock
|
const queryVariables = {
|
||||||
},
|
id: price.address.toLowerCase(),
|
||||||
pollInterval: 20000
|
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(() => {
|
useEffect(() => {
|
||||||
Logger.log('Fired GraphOptions!')
|
Logger.log('Fired GraphOptions!')
|
||||||
@ -145,59 +179,70 @@ export default function Graph(): ReactElement {
|
|||||||
}, [locale, darkMode.value])
|
}, [locale, darkMode.value])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) return
|
getPoolHistory()
|
||||||
Logger.log('Fired GraphData!')
|
}, [lastBlock])
|
||||||
|
|
||||||
const latestTimestamps = [
|
useEffect(() => {
|
||||||
...timestamps,
|
async function init() {
|
||||||
...data.poolTransactions.map((item) => {
|
const data: PoolHistory = dataHistory
|
||||||
const date = new Date(item.timestamp * 1000)
|
if (!data) {
|
||||||
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
await getPoolHistory()
|
||||||
})
|
return
|
||||||
]
|
}
|
||||||
setTimestamps(latestTimestamps)
|
Logger.log('Fired GraphData!')
|
||||||
|
|
||||||
const latestLiquidtyHistory = [
|
const latestTimestamps = [
|
||||||
...liquidityHistory,
|
...timestamps,
|
||||||
...data.poolTransactions.map((item) => item.oceanReserve)
|
...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 = [
|
setLiquidityHistory(latestLiquidtyHistory)
|
||||||
...priceHistory,
|
|
||||||
...data.poolTransactions.map((item) => item.spotPrice)
|
|
||||||
]
|
|
||||||
|
|
||||||
setPriceHistory(latestPriceHistory)
|
const latestPriceHistory = [
|
||||||
|
...priceHistory,
|
||||||
|
...data.poolTransactions.map((item) => item.spotPrice)
|
||||||
|
]
|
||||||
|
|
||||||
if (data.poolTransactions.length > 0) {
|
setPriceHistory(latestPriceHistory)
|
||||||
const newBlock =
|
|
||||||
data.poolTransactions[data.poolTransactions.length - 1].block
|
if (data.poolTransactions.length > 0) {
|
||||||
if (newBlock === lastBlock) return
|
const newBlock =
|
||||||
setLastBlock(
|
data.poolTransactions[data.poolTransactions.length - 1].block
|
||||||
data.poolTransactions[data.poolTransactions.length - 1].block
|
if (newBlock === lastBlock) return
|
||||||
)
|
setLastBlock(
|
||||||
refetch()
|
data.poolTransactions[data.poolTransactions.length - 1].block
|
||||||
} else {
|
)
|
||||||
setGraphData({
|
} else {
|
||||||
labels: latestTimestamps.slice(0),
|
setGraphData({
|
||||||
datasets: [
|
labels: latestTimestamps.slice(0),
|
||||||
{
|
datasets: [
|
||||||
...lineStyle,
|
{
|
||||||
label: 'Liquidity (OCEAN)',
|
...lineStyle,
|
||||||
data:
|
label: 'Liquidity (OCEAN)',
|
||||||
graphType === 'liquidity'
|
data:
|
||||||
? latestLiquidtyHistory.slice(0)
|
graphType === 'liquidity'
|
||||||
: latestPriceHistory.slice(0),
|
? latestLiquidtyHistory.slice(0)
|
||||||
borderColor: `#8b98a9`,
|
: latestPriceHistory.slice(0),
|
||||||
pointBackgroundColor: `#8b98a9`
|
borderColor: `#8b98a9`,
|
||||||
}
|
pointBackgroundColor: `#8b98a9`
|
||||||
]
|
}
|
||||||
})
|
]
|
||||||
setIsLoading(false)
|
})
|
||||||
|
setIsLoading(false)
|
||||||
|
refetchGraph()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [data, graphType])
|
init()
|
||||||
|
}, [dataHistory, graphType])
|
||||||
|
|
||||||
function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) {
|
function handleGraphTypeSwitch(e: ChangeEvent<HTMLButtonElement>) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -11,7 +11,8 @@ export default function TokenList({
|
|||||||
dtSymbol,
|
dtSymbol,
|
||||||
poolShares,
|
poolShares,
|
||||||
conversion,
|
conversion,
|
||||||
highlight
|
highlight,
|
||||||
|
showTVLLabel
|
||||||
}: {
|
}: {
|
||||||
title: string | ReactNode
|
title: string | ReactNode
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
@ -21,6 +22,7 @@ export default function TokenList({
|
|||||||
poolShares: string
|
poolShares: string
|
||||||
conversion: number
|
conversion: number
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
|
showTVLLabel?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.tokenlist} ${highlight ? styles.highlight : ''}`}>
|
<div className={`${styles.tokenlist} ${highlight ? styles.highlight : ''}`}>
|
||||||
@ -33,6 +35,7 @@ export default function TokenList({
|
|||||||
<Conversion
|
<Conversion
|
||||||
price={`${conversion}`}
|
price={`${conversion}`}
|
||||||
className={styles.totalLiquidity}
|
className={styles.totalLiquidity}
|
||||||
|
showTVLLabel={showTVLLabel}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,10 +15,13 @@ import { PoolBalance } from '../../../../@types/TokenBalance'
|
|||||||
import Transactions from './Transactions'
|
import Transactions from './Transactions'
|
||||||
import Graph from './Graph'
|
import Graph from './Graph'
|
||||||
import { useAsset } from '../../../../providers/Asset'
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
import { gql, OperationResult } from 'urql'
|
||||||
import { PoolLiquidity } from '../../../../@types/apollo/PoolLiquidity'
|
import { PoolLiquidity } from '../../../../@types/apollo/PoolLiquidity'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
import { fetchData, getQueryContext } from '../../../../utils/subgraph'
|
||||||
|
|
||||||
|
const REFETCH_INTERVAL = 5000
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query PoolQuery {
|
query PoolQuery {
|
||||||
@ -62,9 +65,10 @@ export default function Pool(): ReactElement {
|
|||||||
const data = useStaticQuery(contentQuery)
|
const data = useStaticQuery(contentQuery)
|
||||||
const content = data.content.edges[0].node.childContentJson.pool
|
const content = data.content.edges[0].node.childContentJson.pool
|
||||||
|
|
||||||
const { accountId, networkId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { isInPurgatory, ddo, owner, price, refreshInterval } = useAsset()
|
const { isInPurgatory, ddo, owner, price, refreshInterval, isAssetNetwork } =
|
||||||
|
useAsset()
|
||||||
const dtSymbol = ddo?.dataTokenInfo.symbol
|
const dtSymbol = ddo?.dataTokenInfo.symbol
|
||||||
|
|
||||||
const [poolTokens, setPoolTokens] = useState<string>()
|
const [poolTokens, setPoolTokens] = useState<string>()
|
||||||
@ -88,20 +92,50 @@ export default function Pool(): ReactElement {
|
|||||||
const [creatorLiquidity, setCreatorLiquidity] = useState<PoolBalance>()
|
const [creatorLiquidity, setCreatorLiquidity] = useState<PoolBalance>()
|
||||||
const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>()
|
const [creatorPoolTokens, setCreatorPoolTokens] = useState<string>()
|
||||||
const [creatorPoolShare, setCreatorPoolShare] = 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
|
// the purpose of the value is just to trigger the effect
|
||||||
const [refreshPool, setRefreshPool] = useState(false)
|
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(),
|
id: price.address.toLowerCase(),
|
||||||
shareId: `${price.address.toLowerCase()}-${ddo.publicKey[0].owner.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(() => {
|
useEffect(() => {
|
||||||
async function init() {
|
async function init() {
|
||||||
if (!dataLiquidity || !dataLiquidity.pool) return
|
if (!dataLiquidity || !dataLiquidity.pool) {
|
||||||
|
await getPoolLiquidity()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Total pool shares
|
// Total pool shares
|
||||||
const totalPoolTokens = dataLiquidity.pool.totalShares
|
const totalPoolTokens = dataLiquidity.pool.totalShares
|
||||||
@ -149,6 +183,7 @@ export default function Pool(): ReactElement {
|
|||||||
creatorLiquidity &&
|
creatorLiquidity &&
|
||||||
((Number(creatorPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
((Number(creatorPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2)
|
||||||
setCreatorPoolShare(creatorPoolShare)
|
setCreatorPoolShare(creatorPoolShare)
|
||||||
|
refetchLiquidity()
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
}, [dataLiquidity, ddo.dataToken, price.datatoken, price.ocean, price?.value])
|
}, [dataLiquidity, ddo.dataToken, price.datatoken, price.ocean, price?.value])
|
||||||
@ -243,15 +278,15 @@ export default function Pool(): ReactElement {
|
|||||||
<Tooltip content={content.tooltips.price} />
|
<Tooltip content={content.tooltips.price} />
|
||||||
<div className={styles.dataTokenLinks}>
|
<div className={styles.dataTokenLinks}>
|
||||||
<ExplorerLink
|
<ExplorerLink
|
||||||
networkId={networkId}
|
networkId={ddo.chainId}
|
||||||
path={`address/${price?.address}`}
|
path={`address/${price?.address}`}
|
||||||
>
|
>
|
||||||
Pool
|
Pool
|
||||||
</ExplorerLink>
|
</ExplorerLink>
|
||||||
<ExplorerLink
|
<ExplorerLink
|
||||||
networkId={networkId}
|
networkId={ddo.chainId}
|
||||||
path={
|
path={
|
||||||
networkId === 137 || networkId === 1287
|
ddo.chainId === 2021000 || ddo.chainId === 1287
|
||||||
? `tokens/${ddo.dataToken}`
|
? `tokens/${ddo.dataToken}`
|
||||||
: `token/${ddo.dataToken}`
|
: `token/${ddo.dataToken}`
|
||||||
}
|
}
|
||||||
@ -284,7 +319,7 @@ export default function Pool(): ReactElement {
|
|||||||
</TokenList>
|
</TokenList>
|
||||||
|
|
||||||
<TokenList
|
<TokenList
|
||||||
title="Pool Creator Liquidity"
|
title="Pool Creator Statistics"
|
||||||
ocean={`${creatorLiquidity?.ocean}`}
|
ocean={`${creatorLiquidity?.ocean}`}
|
||||||
dt={`${creatorLiquidity?.datatoken}`}
|
dt={`${creatorLiquidity?.datatoken}`}
|
||||||
dtSymbol={dtSymbol}
|
dtSymbol={dtSymbol}
|
||||||
@ -314,6 +349,7 @@ export default function Pool(): ReactElement {
|
|||||||
dtSymbol={dtSymbol}
|
dtSymbol={dtSymbol}
|
||||||
poolShares={totalPoolTokens}
|
poolShares={totalPoolTokens}
|
||||||
conversion={totalLiquidityInOcean}
|
conversion={totalLiquidityInOcean}
|
||||||
|
showTVLLabel
|
||||||
>
|
>
|
||||||
<Token symbol="% swap fee" balance={swapFee} noIcon />
|
<Token symbol="% swap fee" balance={swapFee} noIcon />
|
||||||
</TokenList>
|
</TokenList>
|
||||||
@ -330,14 +366,18 @@ export default function Pool(): ReactElement {
|
|||||||
style="primary"
|
style="primary"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setShowAdd(true)}
|
onClick={() => setShowAdd(true)}
|
||||||
disabled={isInPurgatory}
|
disabled={isInPurgatory || !isAssetNetwork}
|
||||||
>
|
>
|
||||||
Add Liquidity
|
Add Liquidity
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasAddedLiquidity && !isRemoveDisabled && (
|
{hasAddedLiquidity && !isRemoveDisabled && (
|
||||||
<Button size="small" onClick={() => setShowRemove(true)}>
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={() => setShowRemove(true)}
|
||||||
|
disabled={!isAssetNetwork}
|
||||||
|
>
|
||||||
Remove
|
Remove
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -14,6 +14,7 @@ import { FormTradeData, initialValues } from '../../../../models/FormTrade'
|
|||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query TradeQuery {
|
query TradeQuery {
|
||||||
@ -49,6 +50,7 @@ export default function FormTrade({
|
|||||||
const content = data.content.edges[0].node.childContentJson.trade
|
const content = data.content.edges[0].node.childContentJson.trade
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
|
const { isAssetNetwork } = useAsset()
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const [txId, setTxId] = useState<string>()
|
const [txId, setTxId] = useState<string>()
|
||||||
|
|
||||||
@ -139,7 +141,7 @@ export default function FormTrade({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Actions
|
<Actions
|
||||||
isDisabled={!isWarningAccepted}
|
isDisabled={!isWarningAccepted || !isAssetNetwork}
|
||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
loaderMessage="Swapping tokens..."
|
loaderMessage="Swapping tokens..."
|
||||||
successMessage="Successfully swapped tokens."
|
successMessage="Successfully swapped tokens."
|
||||||
|
@ -6,8 +6,8 @@ import { useOcean } from '../../../../providers/Ocean'
|
|||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
|
||||||
export default function Trade(): ReactElement {
|
export default function Trade(): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const { accountId, balance } = useWeb3()
|
||||||
const { ocean, balance } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const [tokenBalance, setTokenBalance] = useState<PoolBalance>()
|
const [tokenBalance, setTokenBalance] = useState<PoolBalance>()
|
||||||
const { price, ddo } = useAsset()
|
const { price, ddo } = useAsset()
|
||||||
const [maxDt, setMaxDt] = useState(0)
|
const [maxDt, setMaxDt] = useState(0)
|
||||||
|
@ -11,13 +11,14 @@ import Trade from './Trade'
|
|||||||
import { useAsset } from '../../../providers/Asset'
|
import { useAsset } from '../../../providers/Asset'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
import { useOcean } from '../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
|
import Web3Feedback from '../../molecules/Web3Feedback'
|
||||||
import { getFileInfo } from '../../../utils/provider'
|
import { getFileInfo } from '../../../utils/provider'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export default function AssetActions(): ReactElement {
|
export default function AssetActions(): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const { accountId, balance } = useWeb3()
|
||||||
const { config, ocean, balance, account } = useOcean()
|
const { ocean, config, account } = useOcean()
|
||||||
const { price, ddo } = useAsset()
|
const { price, ddo, isAssetNetwork } = useAsset()
|
||||||
|
|
||||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
||||||
const [dtBalance, setDtBalance] = useState<string>()
|
const [dtBalance, setDtBalance] = useState<string>()
|
||||||
@ -31,7 +32,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ddo || !accountId) return
|
if (!ddo || !accountId) return
|
||||||
async function checkIsConsumable() {
|
async function checkIsConsumable() {
|
||||||
const consumable = await ocean.assets.isConsumable(
|
const consumable: any = await ocean.assets.isConsumable(
|
||||||
ddo,
|
ddo,
|
||||||
accountId.toLowerCase()
|
accountId.toLowerCase()
|
||||||
)
|
)
|
||||||
@ -72,6 +73,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
// Get and set user DT balance
|
// Get and set user DT balance
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ocean || !accountId) return
|
if (!ocean || !accountId) return
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
const dtBalance = await ocean.datatokens.balance(
|
const dtBalance = await ocean.datatokens.balance(
|
||||||
@ -84,7 +86,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
}, [ocean, accountId, ddo.dataToken])
|
}, [ocean, accountId, ddo.dataToken, isAssetNetwork])
|
||||||
|
|
||||||
// Check user balance against price
|
// Check user balance against price
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -141,8 +143,14 @@ export default function AssetActions(): ReactElement {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Permission eventType="consume">
|
<>
|
||||||
<Tabs items={tabs} className={styles.actions} />
|
<Permission eventType="consume">
|
||||||
</Permission>
|
<Tabs items={tabs} className={styles.actions} />
|
||||||
|
</Permission>
|
||||||
|
<Web3Feedback
|
||||||
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
|
isAssetNetwork={isAssetNetwork}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import styles from './AlgorithmDatasetsListForCompute.module.css'
|
|||||||
import { getAlgorithmDatasetsForCompute } from '../../../utils/aquarius'
|
import { getAlgorithmDatasetsForCompute } from '../../../utils/aquarius'
|
||||||
import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection'
|
||||||
import AssetComputeList from '../../molecules/AssetComputeList'
|
import AssetComputeList from '../../molecules/AssetComputeList'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
|
||||||
import { useAsset } from '../../../providers/Asset'
|
import { useAsset } from '../../../providers/Asset'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ export default function AlgorithmDatasetsListForCompute({
|
|||||||
algorithmDid: string
|
algorithmDid: string
|
||||||
dataset: DDO
|
dataset: DDO
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { config } = useOcean()
|
|
||||||
const { type } = useAsset()
|
const { type } = useAsset()
|
||||||
const [datasetsForCompute, setDatasetsForCompute] =
|
const [datasetsForCompute, setDatasetsForCompute] =
|
||||||
useState<AssetSelectionAsset[]>()
|
useState<AssetSelectionAsset[]>()
|
||||||
@ -28,7 +26,7 @@ export default function AlgorithmDatasetsListForCompute({
|
|||||||
const datasets = await getAlgorithmDatasetsForCompute(
|
const datasets = await getAlgorithmDatasetsForCompute(
|
||||||
algorithmDid,
|
algorithmDid,
|
||||||
datasetComputeService?.serviceEndpoint,
|
datasetComputeService?.serviceEndpoint,
|
||||||
config.metadataCacheUri
|
dataset?.chainId
|
||||||
)
|
)
|
||||||
setDatasetsForCompute(datasets)
|
setDatasetsForCompute(datasets)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.bookmark {
|
.bookmark {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10px;
|
top: -3px;
|
||||||
right: calc(var(--spacer) / 8);
|
right: calc(var(--spacer) / 8);
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background: none;
|
background: none;
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
.bookmark:hover,
|
.bookmark:hover,
|
||||||
.bookmark:focus {
|
.bookmark:focus {
|
||||||
transform: translate3d(0, 6px, 0);
|
transform: translate3d(0, -3px, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bookmark.active svg {
|
.bookmark.active svg {
|
||||||
|
@ -2,15 +2,12 @@ import { useUserPreferences } from '../../../providers/UserPreferences'
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Bookmark.module.css'
|
import styles from './Bookmark.module.css'
|
||||||
import { ReactComponent as BookmarkIcon } from '../../../images/bookmark.svg'
|
import { ReactComponent as BookmarkIcon } from '../../../images/bookmark.svg'
|
||||||
import { ConfigHelperConfig } from '@oceanprotocol/lib'
|
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
import { useOcean } from '../../../providers/Ocean'
|
||||||
|
|
||||||
export default function Bookmark({ did }: { did: string }): ReactElement {
|
export default function Bookmark({ did }: { did: string }): ReactElement {
|
||||||
const { config } = useOcean()
|
const { config } = useOcean()
|
||||||
const { bookmarks, addBookmark, removeBookmark } = useUserPreferences()
|
const { bookmarks, addBookmark, removeBookmark } = useUserPreferences()
|
||||||
const isBookmarked =
|
const isBookmarked = bookmarks && bookmarks?.includes(did)
|
||||||
bookmarks &&
|
|
||||||
bookmarks[(config as ConfigHelperConfig).network]?.includes(did)
|
|
||||||
|
|
||||||
function handleBookmark() {
|
function handleBookmark() {
|
||||||
isBookmarked ? removeBookmark(did) : addBookmark(did)
|
isBookmarked ? removeBookmark(did) : addBookmark(did)
|
||||||
|
@ -3,7 +3,7 @@ import { useAsset } from '../../../providers/Asset'
|
|||||||
import ExplorerLink from '../../atoms/ExplorerLink'
|
import ExplorerLink from '../../atoms/ExplorerLink'
|
||||||
import Time from '../../atoms/Time'
|
import Time from '../../atoms/Time'
|
||||||
import styles from './EditHistory.module.css'
|
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 { ReceiptData_datatokens_updates as ReceiptData } from '../../../@types/apollo/ReceiptData'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
|
|
||||||
@ -22,9 +22,11 @@ const getReceipts = gql`
|
|||||||
export default function EditHistory(): ReactElement {
|
export default function EditHistory(): ReactElement {
|
||||||
const { networkId } = useWeb3()
|
const { networkId } = useWeb3()
|
||||||
const { ddo } = useAsset()
|
const { ddo } = useAsset()
|
||||||
const { data } = useQuery(getReceipts, {
|
const [result] = useQuery({
|
||||||
|
query: getReceipts,
|
||||||
variables: { address: ddo?.dataToken.toLowerCase() }
|
variables: { address: ddo?.dataToken.toLowerCase() }
|
||||||
})
|
})
|
||||||
|
const { data } = result
|
||||||
|
|
||||||
const [receipts, setReceipts] = useState<ReceiptData[]>()
|
const [receipts, setReceipts] = useState<ReceiptData[]>()
|
||||||
const [creationTx, setCreationTx] = useState<string>()
|
const [creationTx, setCreationTx] = useState<string>()
|
||||||
@ -48,14 +50,14 @@ export default function EditHistory(): ReactElement {
|
|||||||
<ul className={styles.history}>
|
<ul className={styles.history}>
|
||||||
{receipts?.map((receipt) => (
|
{receipts?.map((receipt) => (
|
||||||
<li key={receipt.id} className={styles.item}>
|
<li key={receipt.id} className={styles.item}>
|
||||||
<ExplorerLink networkId={networkId} path={`/tx/${receipt.tx}`}>
|
<ExplorerLink networkId={ddo.chainId} path={`/tx/${receipt.tx}`}>
|
||||||
edited{' '}
|
edited{' '}
|
||||||
<Time date={receipt.timestamp.toString()} relative isUnix />
|
<Time date={receipt.timestamp.toString()} relative isUnix />
|
||||||
</ExplorerLink>
|
</ExplorerLink>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
<li className={styles.item}>
|
<li className={styles.item}>
|
||||||
<ExplorerLink networkId={networkId} path={`/tx/${creationTx}`}>
|
<ExplorerLink networkId={ddo.chainId} path={`/tx/${creationTx}`}>
|
||||||
published <Time date={ddo.created} relative />
|
published <Time date={ddo.created} relative />
|
||||||
</ExplorerLink>
|
</ExplorerLink>
|
||||||
</li>
|
</li>
|
||||||
|
@ -9,7 +9,7 @@ import AssetType from '../../atoms/AssetType'
|
|||||||
import styles from './MetaMain.module.css'
|
import styles from './MetaMain.module.css'
|
||||||
|
|
||||||
export default function MetaMain(): ReactElement {
|
export default function MetaMain(): ReactElement {
|
||||||
const { ddo, owner, type } = useAsset()
|
const { ddo, owner, type, isAssetNetwork } = useAsset()
|
||||||
const { networkId, web3ProviderInfo } = useWeb3()
|
const { networkId, web3ProviderInfo } = useWeb3()
|
||||||
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
||||||
const accessType = isCompute ? 'compute' : 'access'
|
const accessType = isCompute ? 'compute' : 'access'
|
||||||
@ -34,7 +34,7 @@ export default function MetaMain(): ReactElement {
|
|||||||
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
||||||
</ExplorerLink>
|
</ExplorerLink>
|
||||||
|
|
||||||
{web3ProviderInfo?.name === 'MetaMask' && (
|
{web3ProviderInfo?.name === 'MetaMask' && isAssetNetwork && (
|
||||||
<span className={styles.addWrap}>
|
<span className={styles.addWrap}>
|
||||||
<AddToken
|
<AddToken
|
||||||
address={ddo?.dataTokenInfo.address}
|
address={ddo?.dataTokenInfo.address}
|
||||||
|
@ -23,8 +23,8 @@ export default function Dynamic({
|
|||||||
ddo: DDO
|
ddo: DDO
|
||||||
content: any
|
content: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { networkId } = useWeb3()
|
const { networkId, balance } = useWeb3()
|
||||||
const { account, balance } = useOcean()
|
const { account } = useOcean()
|
||||||
const [firstPrice, setFirstPrice] = useState<string>()
|
const [firstPrice, setFirstPrice] = useState<string>()
|
||||||
|
|
||||||
// Connect with form
|
// Connect with form
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
|
.networkWrap {
|
||||||
|
display: block;
|
||||||
|
margin-top: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: calc(var(--spacer) * 1.5);
|
gap: calc(var(--spacer) * 1.5);
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: -1.5rem;
|
margin-top: -1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid > div {
|
.grid > div {
|
||||||
|
@ -19,6 +19,7 @@ import { useWeb3 } from '../../../providers/Web3'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import EditAdvancedSettings from '../AssetActions/Edit/EditAdvancedSettings'
|
import EditAdvancedSettings from '../AssetActions/Edit/EditAdvancedSettings'
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
|
import NetworkName from '../../atoms/NetworkName'
|
||||||
|
|
||||||
export interface AssetContentProps {
|
export interface AssetContentProps {
|
||||||
path?: string
|
path?: string
|
||||||
@ -46,7 +47,7 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
|||||||
const content = data.purgatory.edges[0].node.childContentJson.asset
|
const content = data.purgatory.edges[0].node.childContentJson.asset
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { owner, isInPurgatory, purgatoryData } = useAsset()
|
const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset()
|
||||||
const [showPricing, setShowPricing] = useState(false)
|
const [showPricing, setShowPricing] = useState(false)
|
||||||
const [showEdit, setShowEdit] = useState<boolean>()
|
const [showEdit, setShowEdit] = useState<boolean>()
|
||||||
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
||||||
@ -87,72 +88,82 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
|||||||
) : showEditAdvancedSettings ? (
|
) : showEditAdvancedSettings ? (
|
||||||
<EditAdvancedSettings setShowEdit={setShowEditAdvancedSettings} />
|
<EditAdvancedSettings setShowEdit={setShowEditAdvancedSettings} />
|
||||||
) : (
|
) : (
|
||||||
<article className={styles.grid}>
|
<>
|
||||||
<div>
|
<div className={styles.networkWrap}>
|
||||||
{showPricing && <Pricing ddo={ddo} />}
|
<NetworkName networkId={ddo.chainId} className={styles.network} />
|
||||||
<div className={styles.content}>
|
</div>
|
||||||
<MetaMain />
|
|
||||||
<Bookmark did={ddo.id} />
|
|
||||||
|
|
||||||
{isInPurgatory ? (
|
<article className={styles.grid}>
|
||||||
<Alert
|
<div>
|
||||||
title={content.title}
|
{showPricing && <Pricing ddo={ddo} />}
|
||||||
badge={`Reason: ${purgatoryData?.reason}`}
|
<div className={styles.content}>
|
||||||
text={content.description}
|
<MetaMain />
|
||||||
state="error"
|
<Bookmark did={ddo.id} />
|
||||||
/>
|
|
||||||
) : (
|
{isInPurgatory ? (
|
||||||
<>
|
<Alert
|
||||||
<Markdown
|
title={content.title}
|
||||||
className={styles.description}
|
badge={`Reason: ${purgatoryData?.reason}`}
|
||||||
text={metadata?.additionalInformation?.description || ''}
|
text={content.description}
|
||||||
|
state="error"
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Markdown
|
||||||
|
className={styles.description}
|
||||||
|
text={metadata?.additionalInformation?.description || ''}
|
||||||
|
/>
|
||||||
|
|
||||||
<MetaSecondary />
|
<MetaSecondary />
|
||||||
|
|
||||||
{isOwner && (
|
{isOwner && isAssetNetwork && (
|
||||||
<div className={styles.ownerActions}>
|
<div className={styles.ownerActions}>
|
||||||
<Button style="text" size="small" onClick={handleEditButton}>
|
<Button
|
||||||
Edit Metadata
|
style="text"
|
||||||
</Button>
|
size="small"
|
||||||
{appConfig.allowAdvancedSettings === 'true' && (
|
onClick={handleEditButton}
|
||||||
<>
|
>
|
||||||
<span className={styles.separator}>|</span>
|
Edit Metadata
|
||||||
<Button
|
</Button>
|
||||||
style="text"
|
{appConfig.allowAdvancedSettings === 'true' && (
|
||||||
size="small"
|
<>
|
||||||
onClick={handleEditAdvancedSettingsButton}
|
<span className={styles.separator}>|</span>
|
||||||
>
|
<Button
|
||||||
Edit Advanced Settings
|
style="text"
|
||||||
</Button>
|
size="small"
|
||||||
</>
|
onClick={handleEditAdvancedSettingsButton}
|
||||||
)}
|
>
|
||||||
{ddo.findServiceByType('compute') && type === 'dataset' && (
|
Edit Advanced Settings
|
||||||
<>
|
</Button>
|
||||||
<span className={styles.separator}>|</span>
|
</>
|
||||||
<Button
|
)}
|
||||||
style="text"
|
{ddo.findServiceByType('compute') && type === 'dataset' && (
|
||||||
size="small"
|
<>
|
||||||
onClick={handleEditComputeButton}
|
<span className={styles.separator}>|</span>
|
||||||
>
|
<Button
|
||||||
Edit Compute Settings
|
style="text"
|
||||||
</Button>
|
size="small"
|
||||||
</>
|
onClick={handleEditComputeButton}
|
||||||
)}
|
>
|
||||||
</div>
|
Edit Compute Settings
|
||||||
)}
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<MetaFull />
|
<MetaFull />
|
||||||
<EditHistory />
|
<EditHistory />
|
||||||
{debug === true && <DebugOutput title="DDO" output={ddo} />}
|
{debug === true && <DebugOutput title="DDO" output={ddo} />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<AssetActions />
|
<AssetActions />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ export default function Footer(): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<SyncStatus /> | <BuildId />
|
{/* <SyncStatus /> | */}
|
||||||
|
<BuildId />
|
||||||
<MarketStats />
|
<MarketStats />
|
||||||
<div className={styles.copyright}>
|
<div className={styles.copyright}>
|
||||||
© {year} <Markdown text={copyright} /> —{' '}
|
© {year} <Markdown text={copyright} /> —{' '}
|
||||||
|
@ -10,6 +10,7 @@ import { retrieveDDO } from '../../../../utils/aquarius'
|
|||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import Results from './Results'
|
import Results from './Results'
|
||||||
import styles from './Details.module.css'
|
import styles from './Details.module.css'
|
||||||
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
|
||||||
function Asset({
|
function Asset({
|
||||||
title,
|
title,
|
||||||
@ -41,7 +42,7 @@ function Asset({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
||||||
const { config } = useOcean()
|
const { appConfig } = useSiteMetadata()
|
||||||
const [algoName, setAlgoName] = useState<string>()
|
const [algoName, setAlgoName] = useState<string>()
|
||||||
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
|
const [algoDtSymbol, setAlgoDtSymbol] = useState<string>()
|
||||||
|
|
||||||
@ -49,18 +50,14 @@ function DetailsAssets({ job }: { job: ComputeJobMetaData }) {
|
|||||||
async function getAlgoMetadata() {
|
async function getAlgoMetadata() {
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
const ddo = await retrieveDDO(
|
const ddo = await retrieveDDO(job.algoDID, source.token)
|
||||||
job.algoDID,
|
|
||||||
config.metadataCacheUri,
|
|
||||||
source.token
|
|
||||||
)
|
|
||||||
setAlgoDtSymbol(ddo.dataTokenInfo.symbol)
|
setAlgoDtSymbol(ddo.dataTokenInfo.symbol)
|
||||||
|
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
setAlgoName(attributes?.main.name)
|
setAlgoName(attributes?.main.name)
|
||||||
}
|
}
|
||||||
getAlgoMetadata()
|
getAlgoMetadata()
|
||||||
}, [config?.metadataCacheUri, job.algoDID])
|
}, [appConfig.metadataCacheUri, job.algoDID])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user