mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
pull from origin main
This commit is contained in:
commit
20361578ca
13
README.md
13
README.md
@ -25,6 +25,7 @@
|
|||||||
- [🛳 Production](#-production)
|
- [🛳 Production](#-production)
|
||||||
- [⬆️ Deployment](#️-deployment)
|
- [⬆️ Deployment](#️-deployment)
|
||||||
- [💖 Contributing](#-contributing)
|
- [💖 Contributing](#-contributing)
|
||||||
|
- [🍴 Forking](#-forking)
|
||||||
- [🏛 License](#-license)
|
- [🏛 License](#-license)
|
||||||
|
|
||||||
## 🏄 Get Started
|
## 🏄 Get Started
|
||||||
@ -358,6 +359,18 @@ We welcome contributions in form of bug reports, feature requests, code changes,
|
|||||||
- [Code of Conduct →](https://docs.oceanprotocol.com/concepts/code-of-conduct/)
|
- [Code of Conduct →](https://docs.oceanprotocol.com/concepts/code-of-conduct/)
|
||||||
- [Reporting Vulnerabilities →](https://docs.oceanprotocol.com/concepts/vulnerabilities/)
|
- [Reporting Vulnerabilities →](https://docs.oceanprotocol.com/concepts/vulnerabilities/)
|
||||||
|
|
||||||
|
## 🍴 Forking
|
||||||
|
|
||||||
|
We encourage you to fork this repository and create your own data marketplace. When you publish your forked version of this market there are a few elements that you are required to change for copyright reasons:
|
||||||
|
|
||||||
|
- The typeface is copyright protected and needs to be changed unless you purchase a license for it.
|
||||||
|
- The Ocean Protocol logo is a trademark of the Ocean Protocol Foundation and must be removed from forked versions of the market.
|
||||||
|
- The name "Ocean Market" is also copyright protected and should be changed to the name of your market.
|
||||||
|
|
||||||
|
Additionally, we would also advise that your retain the text saying "Powered by Ocean Protocol" on your forked version of the marketplace in order to give credit for the development work done by the Ocean Protocol team.
|
||||||
|
|
||||||
|
Everything else is made open according to the apache2 license. We look forward to seeing your data marketplace!
|
||||||
|
|
||||||
## 🏛 License
|
## 🏛 License
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
"label": "Docker Image",
|
"label": "Docker Image",
|
||||||
"placeholder": "e.g. python3.7",
|
"placeholder": "e.g. python3.7",
|
||||||
"help": "Please select an image to run your algorithm.",
|
"help": "Please select an image to run your algorithm.",
|
||||||
"type": "select",
|
"type": "boxSelection",
|
||||||
"options": ["node:latest", "python:latest", "custom image"],
|
"options": [],
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"name": "access",
|
"name": "access",
|
||||||
"label": "Access Type",
|
"label": "Access Type",
|
||||||
"help": "Choose how you want your files to be accessible for the specified price.",
|
"help": "Choose how you want your files to be accessible for the specified price.",
|
||||||
"type": "select",
|
"type": "boxSelection",
|
||||||
"options": ["Download", "Compute"],
|
"options": ["Download", "Compute"],
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -18101,7 +18101,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereumjs-abi": {
|
"ethereumjs-abi": {
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e",
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f",
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.11.8",
|
"bn.js": "^4.11.8",
|
||||||
|
71
src/components/atoms/AddToken.module.css
Normal file
71
src/components/atoms/AddToken.module.css
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover,
|
||||||
|
.button:focus {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoWrap {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoWrap::before {
|
||||||
|
content: '+';
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-family: var(--font-family-base);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
font-size: 1.25em;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.05em;
|
||||||
|
top: 0.05em;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 1.6em;
|
||||||
|
height: 1.6em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: -0.35em;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 0.065rem solid var(--color-secondary);
|
||||||
|
margin-right: calc(var(--spacer) / 10);
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover .logo,
|
||||||
|
.button:focus .logo {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover .logoWrap::before,
|
||||||
|
.button:focus .logoWrap::before {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimal .text {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(-1rem, 0, 0);
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
z-index: 0;
|
||||||
|
white-space: pre;
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
top: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimal:hover .text,
|
||||||
|
.minimal:focus .text {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
53
src/components/atoms/AddToken.tsx
Normal file
53
src/components/atoms/AddToken.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import { addTokenToWallet } from '../../utils/web3'
|
||||||
|
import { useWeb3 } from '../../providers/Web3'
|
||||||
|
import Button from './Button'
|
||||||
|
import styles from './AddToken.module.css'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
export default function AddToken({
|
||||||
|
address,
|
||||||
|
symbol,
|
||||||
|
logo,
|
||||||
|
text,
|
||||||
|
className,
|
||||||
|
minimal
|
||||||
|
}: {
|
||||||
|
address: string
|
||||||
|
symbol: string
|
||||||
|
logo: string // needs to be a remote image
|
||||||
|
text?: string
|
||||||
|
className?: string
|
||||||
|
minimal?: boolean
|
||||||
|
}): ReactElement {
|
||||||
|
const { web3Provider } = useWeb3()
|
||||||
|
|
||||||
|
const styleClasses = cx({
|
||||||
|
button: true,
|
||||||
|
minimal: minimal,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleAddToken() {
|
||||||
|
if (!web3Provider) return
|
||||||
|
|
||||||
|
await addTokenToWallet(web3Provider, address, symbol, logo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={styleClasses}
|
||||||
|
style="text"
|
||||||
|
size="small"
|
||||||
|
onClick={handleAddToken}
|
||||||
|
>
|
||||||
|
<span className={styles.logoWrap}>
|
||||||
|
<img src={logo} className={styles.logo} width="16" height="16" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className={styles.text}>{text || `Add ${symbol}`}</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
@ -1,20 +1,30 @@
|
|||||||
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
|
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
|
||||||
import { ReactComponent as External } from '../../images/external.svg'
|
import { ReactComponent as External } from '../../images/external.svg'
|
||||||
import styles from './ExplorerLink.module.css'
|
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'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function ExplorerLink({
|
export default function ExplorerLink({
|
||||||
path,
|
path,
|
||||||
children
|
children,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
networkId: number
|
networkId: number
|
||||||
path: string
|
path: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { config } = useOcean()
|
const { config } = useOcean()
|
||||||
const [url, setUrl] = useState<string>()
|
const [url, setUrl] = useState<string>()
|
||||||
|
|
||||||
|
const styleClasses = cx({
|
||||||
|
link: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUrl((config as ConfigHelperConfig).explorerUri)
|
setUrl((config as ConfigHelperConfig).explorerUri)
|
||||||
}, [config])
|
}, [config])
|
||||||
@ -25,7 +35,7 @@ export default function ExplorerLink({
|
|||||||
title={`View on ${(config as ConfigHelperConfig).explorerUri}`}
|
title={`View on ${(config as ConfigHelperConfig).explorerUri}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={styles.link}
|
className={styleClasses}
|
||||||
>
|
>
|
||||||
{children} <External />
|
{children} <External />
|
||||||
</a>
|
</a>
|
||||||
|
@ -4,6 +4,9 @@ import styles from './InputElement.module.css'
|
|||||||
import { InputProps } from '.'
|
import { InputProps } from '.'
|
||||||
import FilesInput from '../../molecules/FormFields/FilesInput'
|
import FilesInput from '../../molecules/FormFields/FilesInput'
|
||||||
import Terms from '../../molecules/FormFields/Terms'
|
import Terms from '../../molecules/FormFields/Terms'
|
||||||
|
import BoxSelection, {
|
||||||
|
BoxSelectionOption
|
||||||
|
} from '../../molecules/FormFields/BoxSelection'
|
||||||
import Datatoken from '../../molecules/FormFields/Datatoken'
|
import Datatoken from '../../molecules/FormFields/Datatoken'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import AssetSelection, {
|
import AssetSelection, {
|
||||||
@ -91,7 +94,7 @@ export default function InputElement({
|
|||||||
id={slugify(option)}
|
id={slugify(option)}
|
||||||
type={type}
|
type={type}
|
||||||
name={name}
|
name={name}
|
||||||
checked={props.defaultChecked}
|
defaultChecked={props.defaultChecked}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
<label className={styles.radioLabel} htmlFor={slugify(option)}>
|
<label className={styles.radioLabel} htmlFor={slugify(option)}>
|
||||||
@ -125,6 +128,15 @@ export default function InputElement({
|
|||||||
return <Datatoken name={name} {...field} {...props} />
|
return <Datatoken name={name} {...field} {...props} />
|
||||||
case 'terms':
|
case 'terms':
|
||||||
return <Terms name={name} options={options} {...field} {...props} />
|
return <Terms name={name} options={options} {...field} {...props} />
|
||||||
|
case 'boxSelection':
|
||||||
|
return (
|
||||||
|
<BoxSelection
|
||||||
|
name={name}
|
||||||
|
options={(options as unknown) as BoxSelectionOption[]}
|
||||||
|
{...field}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return prefix || postfix ? (
|
return prefix || postfix ? (
|
||||||
<div className={`${prefix ? styles.prefixGroup : styles.postfixGroup}`}>
|
<div className={`${prefix ? styles.prefixGroup : styles.postfixGroup}`}>
|
||||||
|
@ -68,7 +68,6 @@ export default function Publisher({
|
|||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className={styles.links}>
|
<div className={styles.links}>
|
||||||
{' — '}
|
{' — '}
|
||||||
{profile && (
|
{profile && (
|
||||||
|
@ -2,9 +2,10 @@ import AssetTeaser from '../molecules/AssetTeaser'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import ddo from '../../../tests/unit/__fixtures__/ddo'
|
import ddo from '../../../tests/unit/__fixtures__/ddo'
|
||||||
|
import { AssetListPrices } from '../../utils/subgraph'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Molecules/Asset Teaser'
|
title: 'Molecules/Asset Teaser'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Default = () => <AssetTeaser ddo={ddo as DDO} />
|
export const Default = () => <AssetTeaser ddo={ddo as DDO} price={undefined} />
|
||||||
|
@ -3,7 +3,7 @@ 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 styles from './AssetTeaser.module.css'
|
||||||
import { DDO } 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 Time from '../atoms/Time'
|
||||||
@ -11,9 +11,13 @@ import AssetType from '../atoms/AssetType'
|
|||||||
|
|
||||||
declare type AssetTeaserProps = {
|
declare type AssetTeaserProps = {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
|
price: BestPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
|
const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
||||||
|
ddo,
|
||||||
|
price
|
||||||
|
}: AssetTeaserProps) => {
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
const { name, type } = attributes.main
|
const { name, type } = attributes.main
|
||||||
const { dataTokenInfo } = ddo
|
const { dataTokenInfo } = ddo
|
||||||
@ -47,7 +51,7 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer className={styles.foot}>
|
<footer className={styles.foot}>
|
||||||
<Price price={ddo.price} small />
|
<Price price={price} small />
|
||||||
<p className={styles.date}>
|
<p className={styles.date}>
|
||||||
<Time date={ddo?.created} relative />
|
<Time date={ddo?.created} relative />
|
||||||
</p>
|
</p>
|
||||||
|
57
src/components/molecules/FormFields/BoxSelection.module.css
Normal file
57
src/components/molecules/FormFields/BoxSelection.module.css
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
.boxSelectionsWrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0 calc(var(--spacer) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSelectionsWrapper > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSelection {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 0px;
|
||||||
|
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2)
|
||||||
|
calc(var(--spacer) / 4) calc(var(--spacer) / 2) !important;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSelectionsWrapper input[type='radio'] {
|
||||||
|
position: fixed;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='radio']:checked + label {
|
||||||
|
color: var(--font-color-text);
|
||||||
|
border-color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSelection svg {
|
||||||
|
width: var(--font-size-h4);
|
||||||
|
height: var(--font-size-h4);
|
||||||
|
fill: currentColor;
|
||||||
|
margin-bottom: calc(var(--spacer) / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSelectionsWrapper label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
70
src/components/molecules/FormFields/BoxSelection.tsx
Normal file
70
src/components/molecules/FormFields/BoxSelection.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import React, { ChangeEvent } from 'react'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import Loader from '../../atoms/Loader'
|
||||||
|
import styles from './BoxSelection.module.css'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
export interface BoxSelectionOption {
|
||||||
|
name: string
|
||||||
|
checked: boolean
|
||||||
|
title: JSX.Element | string
|
||||||
|
icon?: JSX.Element
|
||||||
|
text?: JSX.Element | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BoxSelection({
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
disabled,
|
||||||
|
handleChange,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
options: BoxSelectionOption[]
|
||||||
|
disabled?: boolean
|
||||||
|
handleChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
|
}): JSX.Element {
|
||||||
|
const styleClassesWrapper = cx({
|
||||||
|
boxSelectionsWrapper: true,
|
||||||
|
[styles.disabled]: disabled
|
||||||
|
})
|
||||||
|
|
||||||
|
const styleClassesInput = cx({
|
||||||
|
input: true,
|
||||||
|
radio: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styleClassesWrapper}>
|
||||||
|
{!options ? (
|
||||||
|
<Loader />
|
||||||
|
) : (
|
||||||
|
options.map((value: BoxSelectionOption) => (
|
||||||
|
<div key={value.name}>
|
||||||
|
<input
|
||||||
|
id={value.name}
|
||||||
|
type="radio"
|
||||||
|
className={styleClassesInput}
|
||||||
|
defaultChecked={value.checked}
|
||||||
|
onChange={(event) => handleChange(event)}
|
||||||
|
{...props}
|
||||||
|
disabled={disabled}
|
||||||
|
value={value.name}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className={`${styles.boxSelection} ${styles.label}`}
|
||||||
|
htmlFor={value.name}
|
||||||
|
title={value.name}
|
||||||
|
>
|
||||||
|
{value.icon}
|
||||||
|
<span className={styles.title}>{value.title}</span>
|
||||||
|
{value.text}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -2,7 +2,6 @@ import React, { ReactElement, useEffect, useState } from 'react'
|
|||||||
import { useWeb3 } from '../../providers/Web3'
|
import { useWeb3 } from '../../providers/Web3'
|
||||||
import { addCustomNetwork, NetworkObject } from '../../utils/web3'
|
import { addCustomNetwork, NetworkObject } from '../../utils/web3'
|
||||||
import { getOceanConfig } from '../../utils/ocean'
|
import { getOceanConfig } from '../../utils/ocean'
|
||||||
import { getProviderInfo } from 'web3modal'
|
|
||||||
import { useOcean } from '../../providers/Ocean'
|
import { useOcean } from '../../providers/Ocean'
|
||||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||||
import AnnouncementBanner, {
|
import AnnouncementBanner, {
|
||||||
@ -19,7 +18,7 @@ const networkMatic: NetworkObject = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function NetworkBanner(): ReactElement {
|
export default function NetworkBanner(): ReactElement {
|
||||||
const { web3Provider } = useWeb3()
|
const { web3Provider, web3ProviderInfo } = useWeb3()
|
||||||
const { config, connect } = useOcean()
|
const { config, connect } = useOcean()
|
||||||
const { announcement } = useSiteMetadata()
|
const { announcement } = useSiteMetadata()
|
||||||
|
|
||||||
@ -51,10 +50,9 @@ export default function NetworkBanner(): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!web3Provider && !config) return
|
if (!web3ProviderInfo || (!web3Provider && !config)) return
|
||||||
|
|
||||||
const providerInfo = getProviderInfo(web3Provider)
|
switch (web3ProviderInfo.name) {
|
||||||
switch (providerInfo?.name) {
|
|
||||||
case 'Web3':
|
case 'Web3':
|
||||||
if (config.networkId !== 137) {
|
if (config.networkId !== 137) {
|
||||||
setText(announcement.main)
|
setText(announcement.main)
|
||||||
@ -80,7 +78,7 @@ export default function NetworkBanner(): ReactElement {
|
|||||||
setAction(undefined)
|
setAction(undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [web3Provider, config, announcement])
|
}, [web3Provider, web3ProviderInfo, config, announcement])
|
||||||
|
|
||||||
return <AnnouncementBanner text={text} action={action} />
|
return <AnnouncementBanner text={text} action={action} />
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,7 @@
|
|||||||
color: var(--font-color-text);
|
color: var(--font-color-text);
|
||||||
border-color: var(--color-secondary);
|
border-color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.appearances div[class*='boxSelectionsWrapper'] {
|
||||||
|
padding-bottom: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
@ -1,42 +1,44 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, ChangeEvent } from 'react'
|
||||||
import { DarkMode } from 'use-dark-mode'
|
import { DarkMode } from 'use-dark-mode'
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
import FormHelp from '../../atoms/Input/Help'
|
import FormHelp from '../../atoms/Input/Help'
|
||||||
import Label from '../../atoms/Input/Label'
|
import Label from '../../atoms/Input/Label'
|
||||||
import styles from './Appearance.module.css'
|
|
||||||
import { ReactComponent as Moon } from '../../../images/moon.svg'
|
import { ReactComponent as Moon } from '../../../images/moon.svg'
|
||||||
import { ReactComponent as Sun } from '../../../images/sun.svg'
|
import { ReactComponent as Sun } from '../../../images/sun.svg'
|
||||||
|
import BoxSelection, { BoxSelectionOption } from '../FormFields/BoxSelection'
|
||||||
const buttons = ['Light', 'Dark']
|
import styles from './Appearance.module.css'
|
||||||
|
|
||||||
export default function Appearance({
|
export default function Appearance({
|
||||||
darkMode
|
darkMode
|
||||||
}: {
|
}: {
|
||||||
darkMode: DarkMode
|
darkMode: DarkMode
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
const options: BoxSelectionOption[] = [
|
||||||
<li>
|
{
|
||||||
<Label htmlFor="">Appearance</Label>
|
name: 'Light',
|
||||||
<div className={styles.buttons}>
|
checked: !darkMode.value,
|
||||||
{buttons.map((button) => {
|
title: 'Light',
|
||||||
const isDark = button === 'Dark'
|
icon: <Sun />
|
||||||
const selected =
|
},
|
||||||
(isDark && darkMode.value) || (!isDark && !darkMode.value)
|
{
|
||||||
|
name: 'Dark',
|
||||||
|
checked: darkMode.value,
|
||||||
|
title: 'Dark',
|
||||||
|
icon: <Moon />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
function handleChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
<Button
|
event.target.value === 'Dark' ? darkMode.enable() : darkMode.disable()
|
||||||
key={button}
|
}
|
||||||
className={`${styles.button} ${selected ? styles.selected : ''}`}
|
|
||||||
size="small"
|
return (
|
||||||
style="text"
|
<li className={styles.appearances}>
|
||||||
onClick={() => (isDark ? darkMode.enable() : darkMode.disable())}
|
<Label htmlFor="">Appearance</Label>
|
||||||
>
|
<BoxSelection
|
||||||
{isDark ? <Moon /> : <Sun />}
|
options={options}
|
||||||
{button}
|
name="appearanceMode"
|
||||||
</Button>
|
handleChange={handleChange}
|
||||||
)
|
/>
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<FormHelp>Defaults to your OS setting, select to override.</FormHelp>
|
<FormHelp>Defaults to your OS setting, select to override.</FormHelp>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
@ -17,3 +17,14 @@
|
|||||||
.selected {
|
.selected {
|
||||||
composes: selected from './Appearance.module.css';
|
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,53 +1,58 @@
|
|||||||
import { ConfigHelperConfig } from '@oceanprotocol/lib'
|
import { ConfigHelperConfig } from '@oceanprotocol/lib'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, ChangeEvent } from 'react'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
import { useOcean } from '../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
import { getOceanConfig } from '../../../utils/ocean'
|
import { getOceanConfig } from '../../../utils/ocean'
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
import FormHelp from '../../atoms/Input/Help'
|
import FormHelp from '../../atoms/Input/Help'
|
||||||
import Label from '../../atoms/Input/Label'
|
import Label from '../../atoms/Input/Label'
|
||||||
|
import BoxSelection, { BoxSelectionOption } from '../FormFields/BoxSelection'
|
||||||
import styles from './Chain.module.css'
|
import styles from './Chain.module.css'
|
||||||
|
|
||||||
export default function Chain(): ReactElement {
|
export default function Chain(): ReactElement {
|
||||||
const { web3 } = useWeb3()
|
const { web3 } = useWeb3()
|
||||||
const { config, connect } = useOcean()
|
const { config, connect } = useOcean()
|
||||||
|
|
||||||
async function connectOcean(networkName: string) {
|
async function connectOcean(event: ChangeEvent<HTMLInputElement>) {
|
||||||
const config = getOceanConfig(networkName)
|
const config = getOceanConfig(event.target.value)
|
||||||
await connect(config)
|
await connect(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
const chains = [
|
function isNetworkSelected(oceanConfig: string) {
|
||||||
{ name: 'ETH', oceanConfig: 'mainnet', label: 'Mainnet' },
|
return (config as ConfigHelperConfig).network === oceanConfig
|
||||||
{ name: 'Polygon/Matic', oceanConfig: 'polygon', label: 'Mainnet' },
|
}
|
||||||
{ name: 'Moonbase Alpha', oceanConfig: 'moonbeamalpha', label: 'Testnet' }
|
|
||||||
|
const options: BoxSelectionOption[] = [
|
||||||
|
{
|
||||||
|
name: 'mainnet',
|
||||||
|
checked: isNetworkSelected('mainnet'),
|
||||||
|
title: 'ETH',
|
||||||
|
text: 'Mainnet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'polygon',
|
||||||
|
checked: isNetworkSelected('polygon'),
|
||||||
|
title: 'Polygon/Matic',
|
||||||
|
text: 'Mainnet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'moonbeamalpha',
|
||||||
|
checked: isNetworkSelected('moonbeamalpha'),
|
||||||
|
title: 'Moonbase Alpha',
|
||||||
|
text: 'Testnet'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// TODO: to fully solve https://github.com/oceanprotocol/market/issues/432
|
// 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).
|
// 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.
|
// For now, only show the setting for non-wallet users.
|
||||||
return !web3 ? (
|
return !web3 ? (
|
||||||
<li>
|
<li className={styles.chains}>
|
||||||
<Label htmlFor="">Chain</Label>
|
<Label htmlFor="">Chain</Label>
|
||||||
<div className={styles.buttons}>
|
<BoxSelection
|
||||||
{chains.map((button) => {
|
options={options}
|
||||||
const selected =
|
name="chain"
|
||||||
(config as ConfigHelperConfig).network === button.oceanConfig
|
handleChange={connectOcean}
|
||||||
|
/>
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={button.name}
|
|
||||||
className={`${styles.button} ${selected ? styles.selected : ''}`}
|
|
||||||
size="small"
|
|
||||||
style="text"
|
|
||||||
onClick={() => connectOcean(button.oceanConfig)}
|
|
||||||
>
|
|
||||||
{button.name}
|
|
||||||
<span>{button.label}</span>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<FormHelp>Switch the data source for the interface.</FormHelp>
|
<FormHelp>Switch the data source for the interface.</FormHelp>
|
||||||
</li>
|
</li>
|
||||||
) : null
|
) : null
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions span {
|
.walletLogoWrap {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,3 +84,7 @@
|
|||||||
.walletInfo button {
|
.walletInfo button {
|
||||||
margin-top: calc(var(--spacer) / 5) !important;
|
margin-top: calc(var(--spacer) / 5) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addToken {
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
import styles from './Details.module.css'
|
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
|
||||||
import Web3Feedback from './Feedback'
|
|
||||||
import { getProviderInfo, IProviderInfo } from 'web3modal'
|
|
||||||
import Conversion from '../../atoms/Price/Conversion'
|
|
||||||
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 AddToken from '../../atoms/AddToken'
|
||||||
|
import Conversion from '../../atoms/Price/Conversion'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
import { addOceanToWallet } from '../../../utils/web3'
|
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import Web3Feedback from './Feedback'
|
||||||
|
import styles from './Details.module.css'
|
||||||
|
|
||||||
export default function Details(): ReactElement {
|
export default function Details(): ReactElement {
|
||||||
const { web3Provider, connect, logout, networkData } = useWeb3()
|
const {
|
||||||
|
web3Provider,
|
||||||
|
web3ProviderInfo,
|
||||||
|
connect,
|
||||||
|
logout,
|
||||||
|
networkData
|
||||||
|
} = useWeb3()
|
||||||
const { balance, config } = useOcean()
|
const { balance, config } = useOcean()
|
||||||
const { locale } = useUserPreferences()
|
const { locale } = useUserPreferences()
|
||||||
|
|
||||||
const [providerInfo, setProviderInfo] = useState<IProviderInfo>()
|
|
||||||
const [mainCurrency, setMainCurrency] = useState<string>()
|
const [mainCurrency, setMainCurrency] = useState<string>()
|
||||||
// const [portisNetwork, setPortisNetwork] = useState<string>()
|
// const [portisNetwork, setPortisNetwork] = useState<string>()
|
||||||
|
|
||||||
// Workaround cause getInjectedProviderName() always returns `MetaMask`
|
|
||||||
// https://github.com/oceanprotocol/market/issues/332
|
|
||||||
useEffect(() => {
|
|
||||||
if (!web3Provider) return
|
|
||||||
const providerInfo = getProviderInfo(web3Provider)
|
|
||||||
setProviderInfo(providerInfo)
|
|
||||||
}, [web3Provider])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!networkData) return
|
if (!networkData) return
|
||||||
|
|
||||||
@ -61,11 +57,11 @@ export default function Details(): ReactElement {
|
|||||||
|
|
||||||
<li className={styles.actions}>
|
<li className={styles.actions}>
|
||||||
<div title="Connected provider" className={styles.walletInfo}>
|
<div title="Connected provider" className={styles.walletInfo}>
|
||||||
<span>
|
<span className={styles.walletLogoWrap}>
|
||||||
<img className={styles.walletLogo} src={providerInfo?.logo} />
|
<img className={styles.walletLogo} src={web3ProviderInfo?.logo} />
|
||||||
{providerInfo?.name}
|
{web3ProviderInfo?.name}
|
||||||
</span>
|
</span>
|
||||||
{/* {providerInfo?.name === 'Portis' && (
|
{/* {web3ProviderInfo?.name === 'Portis' && (
|
||||||
<InputElement
|
<InputElement
|
||||||
name="network"
|
name="network"
|
||||||
type="select"
|
type="select"
|
||||||
@ -75,20 +71,17 @@ export default function Details(): ReactElement {
|
|||||||
onChange={handlePortisNetworkChange}
|
onChange={handlePortisNetworkChange}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)} */}
|
||||||
{providerInfo?.name === 'MetaMask' && (
|
{web3ProviderInfo?.name === 'MetaMask' && (
|
||||||
<Button
|
<AddToken
|
||||||
style="text"
|
address={config.oceanTokenAddress}
|
||||||
size="small"
|
symbol={config.oceanTokenSymbol}
|
||||||
onClick={() => {
|
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png"
|
||||||
addOceanToWallet(config, web3Provider)
|
className={styles.addToken}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
{`Add ${config.oceanTokenSymbol}`}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
{providerInfo?.name === 'Portis' && (
|
{web3ProviderInfo?.name === 'Portis' && (
|
||||||
<Button
|
<Button
|
||||||
style="text"
|
style="text"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
DDO,
|
DDO,
|
||||||
File as FileMetadata,
|
File as FileMetadata,
|
||||||
Logger,
|
Logger,
|
||||||
ServiceType,
|
|
||||||
publisherTrustedAlgorithm,
|
publisherTrustedAlgorithm,
|
||||||
BestPrice
|
BestPrice
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
@ -37,11 +36,8 @@ import FormStartComputeDataset from './FormComputeDataset'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||||
import Button from '../../../atoms/Button'
|
import Button from '../../../atoms/Button'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
|
||||||
import { FrePrice } from '../../../../@types/apollo/FrePrice'
|
|
||||||
import { PoolPrice } from '../../../../@types/apollo/PoolPrice'
|
|
||||||
import { secondsToString } from '../../../../utils/metadata'
|
import { secondsToString } from '../../../../utils/metadata'
|
||||||
import { getPreviousOrders } from '../../../../utils/subgraph'
|
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
||||||
|
|
||||||
const SuccessAction = () => (
|
const SuccessAction = () => (
|
||||||
<Button style="text" to="/history" size="small">
|
<Button style="text" to="/history" size="small">
|
||||||
@ -49,23 +45,6 @@ const SuccessAction = () => (
|
|||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const freQuery = gql`
|
|
||||||
query AlgorithmFrePrice($datatoken: String) {
|
|
||||||
fixedRateExchanges(orderBy: id, where: { datatoken: $datatoken }) {
|
|
||||||
rate
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const poolQuery = gql`
|
|
||||||
query AlgorithmPoolPrice($datatoken: String) {
|
|
||||||
pools(where: { datatokenAddress: $datatoken }) {
|
|
||||||
spotPrice
|
|
||||||
consumePrice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default function Compute({
|
export default function Compute({
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
@ -95,7 +74,6 @@ export default function Compute({
|
|||||||
)
|
)
|
||||||
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
|
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
|
||||||
const [algorithmPrice, setAlgorithmPrice] = useState<BestPrice>()
|
const [algorithmPrice, setAlgorithmPrice] = useState<BestPrice>()
|
||||||
const [variables, setVariables] = useState({})
|
|
||||||
const [
|
const [
|
||||||
previousAlgorithmOrderId,
|
previousAlgorithmOrderId,
|
||||||
setPreviousAlgorithmOrderId
|
setPreviousAlgorithmOrderId
|
||||||
@ -103,25 +81,6 @@ export default function Compute({
|
|||||||
const [datasetTimeout, setDatasetTimeout] = useState<string>()
|
const [datasetTimeout, setDatasetTimeout] = useState<string>()
|
||||||
const [algorithmTimeout, setAlgorithmTimeout] = useState<string>()
|
const [algorithmTimeout, setAlgorithmTimeout] = useState<string>()
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
const {
|
|
||||||
refetch: refetchFre,
|
|
||||||
startPolling: startPollingFre,
|
|
||||||
data: frePrice
|
|
||||||
} = useQuery<FrePrice>(freQuery, {
|
|
||||||
variables,
|
|
||||||
skip: false
|
|
||||||
})
|
|
||||||
const {
|
|
||||||
refetch: refetchPool,
|
|
||||||
startPolling: startPollingPool,
|
|
||||||
data: poolPrice
|
|
||||||
} = useQuery<PoolPrice>(poolQuery, {
|
|
||||||
variables,
|
|
||||||
skip: false
|
|
||||||
})
|
|
||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
||||||
|
|
||||||
const isComputeButtonDisabled =
|
const isComputeButtonDisabled =
|
||||||
isJobStarting === true || file === null || !ocean || !isBalanceSufficient
|
isJobStarting === true || file === null || !ocean || !isBalanceSufficient
|
||||||
const hasDatatoken = Number(dtBalance) >= 1
|
const hasDatatoken = Number(dtBalance) >= 1
|
||||||
@ -167,7 +126,7 @@ export default function Compute({
|
|||||||
const algorithmQuery =
|
const algorithmQuery =
|
||||||
trustedAlgorithmList.length > 0 ? `(${algoQuerry}) AND` : ``
|
trustedAlgorithmList.length > 0 ? `(${algoQuerry}) AND` : ``
|
||||||
const query = {
|
const query = {
|
||||||
page: 1,
|
offset: 500,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
query_string: {
|
||||||
query: `${algorithmQuery} service.attributes.main.type:algorithm -isInPurgatory:true`
|
query: `${algorithmQuery} service.attributes.main.type:algorithm -isInPurgatory:true`
|
||||||
@ -215,40 +174,10 @@ export default function Compute({
|
|||||||
setDatasetTimeout(secondsToString(timeout))
|
setDatasetTimeout(secondsToString(timeout))
|
||||||
}, [ddo])
|
}, [ddo])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!frePrice ||
|
|
||||||
frePrice.fixedRateExchanges.length === 0 ||
|
|
||||||
algorithmPrice.type !== 'exchange'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
setAlgorithmPrice((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
value: frePrice.fixedRateExchanges[0].rate,
|
|
||||||
address: frePrice.fixedRateExchanges[0].id
|
|
||||||
}))
|
|
||||||
}, [frePrice])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!poolPrice ||
|
|
||||||
poolPrice.pools.length === 0 ||
|
|
||||||
algorithmPrice.type !== 'pool'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
setAlgorithmPrice((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
value:
|
|
||||||
poolPrice.pools[0].consumePrice === '-1'
|
|
||||||
? poolPrice.pools[0].spotPrice
|
|
||||||
: poolPrice.pools[0].consumePrice
|
|
||||||
}))
|
|
||||||
}, [poolPrice])
|
|
||||||
|
|
||||||
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
||||||
if (!ddo) return
|
if (!ddo) return
|
||||||
setAlgorithmPrice(ddo.price)
|
const price = await getPrice(ddo)
|
||||||
setVariables({ datatoken: ddo?.dataToken.toLowerCase() })
|
setAlgorithmPrice(price)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -430,7 +359,8 @@ export default function Compute({
|
|||||||
|
|
||||||
Logger.log('[compute] Starting compute job response: ', response)
|
Logger.log('[compute] Starting compute job response: ', response)
|
||||||
|
|
||||||
setHasPreviousDatasetOrder(true)
|
await checkPreviousOrders(selectedAlgorithmAsset)
|
||||||
|
await checkPreviousOrders(ddo)
|
||||||
setIsPublished(true)
|
setIsPublished(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Failed to start job!')
|
setError('Failed to start job!')
|
||||||
|
@ -44,7 +44,7 @@ export default function FormEditComputeDataset({
|
|||||||
): Promise<AssetSelectionAsset[]> {
|
): Promise<AssetSelectionAsset[]> {
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
const query = {
|
const query = {
|
||||||
page: 1,
|
offset: 500,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
query_string: {
|
||||||
query: `service.attributes.main.type:algorithm -isInPurgatory:true`
|
query: `service.attributes.main.type:algorithm -isInPurgatory:true`
|
||||||
|
@ -81,6 +81,8 @@ export default function Remove({
|
|||||||
const [minOceanAmount, setMinOceanAmount] = useState<string>('0')
|
const [minOceanAmount, setMinOceanAmount] = useState<string>('0')
|
||||||
const [minDatatokenAmount, setMinDatatokenAmount] = useState<string>('0')
|
const [minDatatokenAmount, setMinDatatokenAmount] = useState<string>('0')
|
||||||
|
|
||||||
|
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
|
||||||
|
|
||||||
async function handleRemoveLiquidity() {
|
async function handleRemoveLiquidity() {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
@ -157,12 +159,18 @@ export default function Remove({
|
|||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const minOceanAmount =
|
const minOceanAmount = new Decimal(amountOcean)
|
||||||
(Number(amountOcean) * (100 - Number(slippage))) / 100
|
.mul(new Decimal(100).minus(new Decimal(slippage)))
|
||||||
const minDatatokenAmount =
|
.dividedBy(100)
|
||||||
(Number(amountDatatoken) * (100 - Number(slippage))) / 100
|
.toString()
|
||||||
setMinOceanAmount(`${minOceanAmount}`)
|
|
||||||
setMinDatatokenAmount(`${minDatatokenAmount}`)
|
const minDatatokenAmount = new Decimal(amountDatatoken)
|
||||||
|
.mul(new Decimal(100).minus(new Decimal(slippage)))
|
||||||
|
.dividedBy(100)
|
||||||
|
.toString()
|
||||||
|
|
||||||
|
setMinOceanAmount(minOceanAmount.slice(0, 18))
|
||||||
|
setMinDatatokenAmount(minDatatokenAmount.slice(0, 18))
|
||||||
}, [slippage, amountOcean, amountDatatoken, isAdvanced])
|
}, [slippage, amountOcean, amountDatatoken, isAdvanced])
|
||||||
|
|
||||||
// Set amountPoolShares based on set slider value
|
// Set amountPoolShares based on set slider value
|
||||||
@ -173,9 +181,9 @@ export default function Remove({
|
|||||||
const amountPoolShares = new Decimal(e.target.value)
|
const amountPoolShares = new Decimal(e.target.value)
|
||||||
.dividedBy(100)
|
.dividedBy(100)
|
||||||
.mul(new Decimal(poolTokens))
|
.mul(new Decimal(poolTokens))
|
||||||
.toPrecision(18) // in some cases the returned value contain more than 18 digits which break conversion to wei
|
.toString()
|
||||||
|
|
||||||
setAmountPoolShares(`${amountPoolShares}`)
|
setAmountPoolShares(`${amountPoolShares.slice(0, 18)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMaxButton(e: ChangeEvent<HTMLInputElement>) {
|
function handleMaxButton(e: ChangeEvent<HTMLInputElement>) {
|
||||||
@ -185,9 +193,9 @@ export default function Remove({
|
|||||||
const amountPoolShares = new Decimal(amountMaxPercent)
|
const amountPoolShares = new Decimal(amountMaxPercent)
|
||||||
.dividedBy(100)
|
.dividedBy(100)
|
||||||
.mul(new Decimal(poolTokens))
|
.mul(new Decimal(poolTokens))
|
||||||
.toPrecision(18)
|
.toString()
|
||||||
|
|
||||||
setAmountPoolShares(`${amountPoolShares}`)
|
setAmountPoolShares(`${amountPoolShares.slice(0, 18)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAdvancedButton(e: FormEvent<HTMLButtonElement>) {
|
function handleAdvancedButton(e: FormEvent<HTMLButtonElement>) {
|
||||||
|
@ -7,7 +7,7 @@ import { useAsset } from '../../../../providers/Asset'
|
|||||||
|
|
||||||
export default function Transactions(): ReactElement {
|
export default function Transactions(): ReactElement {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const { ddo } = useAsset()
|
const { price } = useAsset()
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
setOpen(!open)
|
setOpen(!open)
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ export default function Transactions(): ReactElement {
|
|||||||
</Button>
|
</Button>
|
||||||
</h3>
|
</h3>
|
||||||
{open === true && (
|
{open === true && (
|
||||||
<PoolTransactions poolAddress={ddo.price?.address} minimal />
|
<PoolTransactions poolAddress={price?.address} minimal />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -95,8 +95,8 @@ export default function Pool(): ReactElement {
|
|||||||
const [refreshPool, setRefreshPool] = useState(false)
|
const [refreshPool, setRefreshPool] = useState(false)
|
||||||
const { data: dataLiquidity } = useQuery<PoolLiquidity>(poolLiquidityQuery, {
|
const { data: dataLiquidity } = useQuery<PoolLiquidity>(poolLiquidityQuery, {
|
||||||
variables: {
|
variables: {
|
||||||
id: ddo.price.address.toLowerCase(),
|
id: price.address.toLowerCase(),
|
||||||
shareId: `${ddo.price.address.toLowerCase()}-${ddo.publicKey[0].owner.toLowerCase()}`
|
shareId: `${price.address.toLowerCase()}-${ddo.publicKey[0].owner.toLowerCase()}`
|
||||||
},
|
},
|
||||||
pollInterval: 5000
|
pollInterval: 5000
|
||||||
})
|
})
|
||||||
@ -170,7 +170,8 @@ export default function Pool(): ReactElement {
|
|||||||
const totalUserLiquidityInOcean =
|
const totalUserLiquidityInOcean =
|
||||||
userLiquidity?.ocean + userLiquidity?.datatoken * price?.value
|
userLiquidity?.ocean + userLiquidity?.datatoken * price?.value
|
||||||
setTotalUserLiquidityInOcean(totalUserLiquidityInOcean)
|
setTotalUserLiquidityInOcean(totalUserLiquidityInOcean)
|
||||||
const totalLiquidityInOcean = price?.ocean + price?.datatoken * price?.value
|
const totalLiquidityInOcean =
|
||||||
|
Number(price?.ocean) + Number(price?.datatoken) * Number(price?.value)
|
||||||
setTotalLiquidityInOcean(totalLiquidityInOcean)
|
setTotalLiquidityInOcean(totalLiquidityInOcean)
|
||||||
}, [userLiquidity, price, poolTokens, totalPoolTokens])
|
}, [userLiquidity, price, poolTokens, totalPoolTokens])
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ export default function AssetActions(): ReactElement {
|
|||||||
|
|
||||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
||||||
const [dtBalance, setDtBalance] = useState<string>()
|
const [dtBalance, setDtBalance] = useState<string>()
|
||||||
|
|
||||||
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
const isCompute = Boolean(ddo?.findServiceByType('compute'))
|
||||||
|
|
||||||
// Get and set user DT balance
|
// Get and set user DT balance
|
||||||
@ -75,10 +74,7 @@ export default function AssetActions(): ReactElement {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Check from metadata, cause that is available earlier
|
price?.type === 'pool' &&
|
||||||
const hasPool = ddo?.price?.type === 'pool'
|
|
||||||
|
|
||||||
hasPool &&
|
|
||||||
tabs.push(
|
tabs.push(
|
||||||
{
|
{
|
||||||
title: 'Pool',
|
title: 'Pool',
|
||||||
|
@ -27,6 +27,11 @@
|
|||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.datatoken {
|
||||||
|
white-space: pre;
|
||||||
|
margin-right: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
.byline {
|
.byline {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
@ -34,3 +39,13 @@
|
|||||||
.updated {
|
.updated {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addWrap {
|
||||||
|
padding-left: calc(var(--spacer) / 5);
|
||||||
|
border-left: 1px solid var(--border-color);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
}
|
||||||
|
@ -3,13 +3,14 @@ import { useAsset } from '../../../providers/Asset'
|
|||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
import ExplorerLink from '../../atoms/ExplorerLink'
|
import ExplorerLink from '../../atoms/ExplorerLink'
|
||||||
import Publisher from '../../atoms/Publisher'
|
import Publisher from '../../atoms/Publisher'
|
||||||
|
import AddToken from '../../atoms/AddToken'
|
||||||
import Time from '../../atoms/Time'
|
import Time from '../../atoms/Time'
|
||||||
import styles from './MetaMain.module.css'
|
|
||||||
import AssetType from '../../atoms/AssetType'
|
import AssetType from '../../atoms/AssetType'
|
||||||
|
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 } = useAsset()
|
||||||
const { networkId } = 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'
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ export default function MetaMain(): ReactElement {
|
|||||||
className={styles.assetType}
|
className={styles.assetType}
|
||||||
/>
|
/>
|
||||||
<ExplorerLink
|
<ExplorerLink
|
||||||
|
className={styles.datatoken}
|
||||||
networkId={networkId}
|
networkId={networkId}
|
||||||
path={
|
path={
|
||||||
networkId === 137 || networkId === 1287
|
networkId === 137 || networkId === 1287
|
||||||
@ -31,6 +33,19 @@ export default function MetaMain(): ReactElement {
|
|||||||
>
|
>
|
||||||
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
{`${ddo?.dataTokenInfo.name} — ${ddo?.dataTokenInfo.symbol}`}
|
||||||
</ExplorerLink>
|
</ExplorerLink>
|
||||||
|
|
||||||
|
{web3ProviderInfo?.name === 'MetaMask' && (
|
||||||
|
<span className={styles.addWrap}>
|
||||||
|
<AddToken
|
||||||
|
address={ddo?.dataTokenInfo.address}
|
||||||
|
symbol={ddo?.dataTokenInfo.symbol}
|
||||||
|
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/datatoken.png"
|
||||||
|
text={`Add ${ddo?.dataTokenInfo.symbol} to wallet`}
|
||||||
|
className={styles.add}
|
||||||
|
minimal
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={styles.byline}>
|
<div className={styles.byline}>
|
||||||
|
@ -16,3 +16,9 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loaderWrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
@ -1,17 +1,28 @@
|
|||||||
import AssetTeaser from '../molecules/AssetTeaser'
|
import AssetTeaser from '../molecules/AssetTeaser'
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import Pagination from '../molecules/Pagination'
|
import Pagination from '../molecules/Pagination'
|
||||||
import styles from './AssetList.module.css'
|
import styles from './AssetList.module.css'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
|
import { getAssetsBestPrices, AssetListPrices } from '../../utils/subgraph'
|
||||||
|
import Loader from '../atoms/Loader'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
function LoaderArea() {
|
||||||
|
return (
|
||||||
|
<div className={styles.loaderWrap}>
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
declare type AssetListProps = {
|
declare type AssetListProps = {
|
||||||
assets: DDO[]
|
assets: DDO[]
|
||||||
showPagination: boolean
|
showPagination: boolean
|
||||||
page?: number
|
page?: number
|
||||||
totalPages?: number
|
totalPages?: number
|
||||||
|
isLoading?: boolean
|
||||||
onPageChange?: React.Dispatch<React.SetStateAction<number>>
|
onPageChange?: React.Dispatch<React.SetStateAction<number>>
|
||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
@ -21,9 +32,22 @@ const AssetList: React.FC<AssetListProps> = ({
|
|||||||
showPagination,
|
showPagination,
|
||||||
page,
|
page,
|
||||||
totalPages,
|
totalPages,
|
||||||
|
isLoading,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
className
|
className
|
||||||
}) => {
|
}) => {
|
||||||
|
const [assetsWithPrices, setAssetWithPrices] = useState<AssetListPrices[]>()
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!assets) return
|
||||||
|
isLoading && setLoading(true)
|
||||||
|
getAssetsBestPrices(assets).then((asset) => {
|
||||||
|
setAssetWithPrices(asset)
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [assets])
|
||||||
|
|
||||||
// // This changes the page field inside the query
|
// // This changes the page field inside the query
|
||||||
function handlePageChange(selected: number) {
|
function handlePageChange(selected: number) {
|
||||||
onPageChange(selected + 1)
|
onPageChange(selected + 1)
|
||||||
@ -34,11 +58,19 @@ const AssetList: React.FC<AssetListProps> = ({
|
|||||||
[className]: className
|
[className]: className
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return assetsWithPrices &&
|
||||||
|
!loading &&
|
||||||
|
(isLoading === undefined || isLoading === false) ? (
|
||||||
<>
|
<>
|
||||||
<div className={styleClasses}>
|
<div className={styleClasses}>
|
||||||
{assets.length > 0 ? (
|
{assetsWithPrices.length > 0 ? (
|
||||||
assets.map((ddo) => <AssetTeaser ddo={ddo} key={ddo.id} />)
|
assetsWithPrices.map((assetWithPrice) => (
|
||||||
|
<AssetTeaser
|
||||||
|
ddo={assetWithPrice.ddo}
|
||||||
|
price={assetWithPrice.price}
|
||||||
|
key={assetWithPrice.ddo.id}
|
||||||
|
/>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.empty}>No results found.</div>
|
<div className={styles.empty}>No results found.</div>
|
||||||
)}
|
)}
|
||||||
@ -52,6 +84,8 @@ const AssetList: React.FC<AssetListProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
<LoaderArea />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,3 +2,25 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.computeTableContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh,
|
||||||
|
.refresh:first-child {
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh svg {
|
||||||
|
display: inline-block;
|
||||||
|
fill: currentColor;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
margin-right: calc(var(--spacer) / 6);
|
||||||
|
margin-bottom: -0.1rem;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { DDO, Logger, Service, Provider } from '@oceanprotocol/lib'
|
|||||||
import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData'
|
import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData'
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
import Dotdotdot from 'react-dotdotdot'
|
||||||
import Table from '../../../atoms/Table'
|
import Table from '../../../atoms/Table'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../providers/Ocean'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
import { gql, useQuery } from '@apollo/client'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
@ -13,8 +14,9 @@ import { queryMetadata } from '../../../../utils/aquarius'
|
|||||||
import axios, { CancelToken } from 'axios'
|
import axios, { CancelToken } from 'axios'
|
||||||
import { ComputeOrders } from '../../../../@types/apollo/ComputeOrders'
|
import { ComputeOrders } from '../../../../@types/apollo/ComputeOrders'
|
||||||
import Details from './Details'
|
import Details from './Details'
|
||||||
import styles from './index.module.css'
|
|
||||||
import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
||||||
|
import { ReactComponent as Refresh } from '../../../../images/refresh.svg'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const getComputeOrders = gql`
|
const getComputeOrders = gql`
|
||||||
query ComputeOrders($user: String!) {
|
query ComputeOrders($user: String!) {
|
||||||
@ -99,7 +101,7 @@ async function getAssetMetadata(
|
|||||||
export default function ComputeJobs(): ReactElement {
|
export default function ComputeJobs(): ReactElement {
|
||||||
const { ocean, account, config } = useOcean()
|
const { ocean, account, config } = useOcean()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
||||||
const { data } = useQuery<ComputeOrders>(getComputeOrders, {
|
const { data } = useQuery<ComputeOrders>(getComputeOrders, {
|
||||||
variables: {
|
variables: {
|
||||||
@ -107,139 +109,158 @@ export default function ComputeJobs(): ReactElement {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
async function getJobs() {
|
||||||
if (data === undefined || !config?.metadataCacheUri) return
|
if (!ocean || !account) return
|
||||||
|
|
||||||
async function getJobs() {
|
setIsLoading(true)
|
||||||
if (!ocean || !account) return
|
|
||||||
|
|
||||||
setIsLoading(true)
|
const dtList = []
|
||||||
|
const computeJobs: ComputeJobMetaData[] = []
|
||||||
|
for (let i = 0; i < data.tokenOrders.length; i++) {
|
||||||
|
dtList.push(data.tokenOrders[i].datatokenId.address)
|
||||||
|
}
|
||||||
|
const queryDtList = JSON.stringify(dtList)
|
||||||
|
.replace(/,/g, ' ')
|
||||||
|
.replace(/"/g, '')
|
||||||
|
.replace(/(\[|\])/g, '')
|
||||||
|
|
||||||
const dtList = []
|
try {
|
||||||
const computeJobs: ComputeJobMetaData[] = []
|
const source = axios.CancelToken.source()
|
||||||
|
const assets = await getAssetMetadata(
|
||||||
|
queryDtList,
|
||||||
|
config.metadataCacheUri,
|
||||||
|
source.token
|
||||||
|
)
|
||||||
|
const providers: Provider[] = []
|
||||||
|
const serviceEndpoints: string[] = []
|
||||||
for (let i = 0; i < data.tokenOrders.length; i++) {
|
for (let i = 0; i < data.tokenOrders.length; i++) {
|
||||||
dtList.push(data.tokenOrders[i].datatokenId.address)
|
try {
|
||||||
|
const did = web3.utils
|
||||||
|
.toChecksumAddress(data.tokenOrders[i].datatokenId.address)
|
||||||
|
.replace('0x', 'did:op:')
|
||||||
|
|
||||||
|
const ddo = assets.filter((x) => x.id === did)[0]
|
||||||
|
|
||||||
|
if (!ddo) continue
|
||||||
|
|
||||||
|
const service = ddo.service.filter(
|
||||||
|
(x: Service) => x.index === data.tokenOrders[i].serviceId
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
if (!service || service.type !== 'compute') continue
|
||||||
|
const { serviceEndpoint } = service
|
||||||
|
|
||||||
|
const wasProviderQueried =
|
||||||
|
serviceEndpoints.filter((x) => x === serviceEndpoint).length > 0
|
||||||
|
|
||||||
|
if (wasProviderQueried) continue
|
||||||
|
serviceEndpoints.push(serviceEndpoint)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const queryDtList = JSON.stringify(dtList)
|
|
||||||
.replace(/,/g, ' ')
|
|
||||||
.replace(/"/g, '')
|
|
||||||
.replace(/(\[|\])/g, '')
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const source = axios.CancelToken.source()
|
for (let i = 0; i < serviceEndpoints.length; i++) {
|
||||||
const assets = await getAssetMetadata(
|
const instanceConfig = {
|
||||||
queryDtList,
|
config,
|
||||||
config.metadataCacheUri,
|
web3: config.web3Provider,
|
||||||
source.token
|
logger: Logger,
|
||||||
)
|
ocean: ocean
|
||||||
const providers: Provider[] = []
|
}
|
||||||
const serviceEndpoints: string[] = []
|
const provider = await Provider.getInstance(instanceConfig)
|
||||||
for (let i = 0; i < data.tokenOrders.length; i++) {
|
await provider.setBaseUrl(serviceEndpoints[i])
|
||||||
try {
|
const hasSameCompute =
|
||||||
const did = web3.utils
|
providers.filter(
|
||||||
.toChecksumAddress(data.tokenOrders[i].datatokenId.address)
|
(x) => x.computeAddress === provider.computeAddress
|
||||||
.replace('0x', 'did:op:')
|
).length > 0
|
||||||
|
if (!hasSameCompute) providers.push(provider)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
}
|
||||||
|
for (let i = 0; i < providers.length; i++) {
|
||||||
|
try {
|
||||||
|
const providerComputeJobs = (await providers[i].computeStatus(
|
||||||
|
'',
|
||||||
|
account,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
false
|
||||||
|
)) as ComputeJob[]
|
||||||
|
|
||||||
|
// means the provider uri is not good, so we ignore it and move on
|
||||||
|
if (!providerComputeJobs) continue
|
||||||
|
providerComputeJobs.sort((a, b) => {
|
||||||
|
if (a.dateCreated > b.dateCreated) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (a.dateCreated < b.dateCreated) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let j = 0; j < providerComputeJobs.length; j++) {
|
||||||
|
const job = providerComputeJobs[j]
|
||||||
|
const did = job.inputDID[0]
|
||||||
const ddo = assets.filter((x) => x.id === did)[0]
|
const ddo = assets.filter((x) => x.id === did)[0]
|
||||||
|
|
||||||
if (!ddo) continue
|
if (!ddo) continue
|
||||||
|
const serviceMetadata = ddo.service.filter(
|
||||||
const service = ddo.service.filter(
|
(x: Service) => x.type === 'metadata'
|
||||||
(x: Service) => x.index === data.tokenOrders[i].serviceId
|
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
if (!service || service.type !== 'compute') continue
|
const compJob: ComputeJobMetaData = {
|
||||||
const { serviceEndpoint } = service
|
...job,
|
||||||
|
assetName: serviceMetadata.attributes.main.name,
|
||||||
const wasProviderQueried =
|
assetDtSymbol: ddo.dataTokenInfo.symbol
|
||||||
serviceEndpoints.filter((x) => x === serviceEndpoint).length > 0
|
|
||||||
|
|
||||||
if (wasProviderQueried) continue
|
|
||||||
serviceEndpoints.push(serviceEndpoint)
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < serviceEndpoints.length; i++) {
|
|
||||||
const instanceConfig = {
|
|
||||||
config,
|
|
||||||
web3: config.web3Provider,
|
|
||||||
logger: Logger,
|
|
||||||
ocean: ocean
|
|
||||||
}
|
}
|
||||||
const provider = await Provider.getInstance(instanceConfig)
|
computeJobs.push(compJob)
|
||||||
await provider.setBaseUrl(serviceEndpoints[i])
|
|
||||||
const hasSameCompute =
|
|
||||||
providers.filter(
|
|
||||||
(x) => x.computeAddress === provider.computeAddress
|
|
||||||
).length > 0
|
|
||||||
if (!hasSameCompute) providers.push(provider)
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error(err)
|
Logger.error(err)
|
||||||
}
|
}
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
try {
|
|
||||||
const providerComputeJobs = (await providers[i].computeStatus(
|
|
||||||
'',
|
|
||||||
account,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
false
|
|
||||||
)) as ComputeJob[]
|
|
||||||
|
|
||||||
// means the provider uri is not good, so we ignore it and move on
|
|
||||||
if (!providerComputeJobs) continue
|
|
||||||
providerComputeJobs.sort((a, b) => {
|
|
||||||
if (a.dateCreated > b.dateCreated) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if (a.dateCreated < b.dateCreated) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
|
|
||||||
for (let j = 0; j < providerComputeJobs.length; j++) {
|
|
||||||
const job = providerComputeJobs[j]
|
|
||||||
const did = job.inputDID[0]
|
|
||||||
const ddo = assets.filter((x) => x.id === did)[0]
|
|
||||||
|
|
||||||
if (!ddo) continue
|
|
||||||
const serviceMetadata = ddo.service.filter(
|
|
||||||
(x: Service) => x.type === 'metadata'
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
const compJob: ComputeJobMetaData = {
|
|
||||||
...job,
|
|
||||||
assetName: serviceMetadata.attributes.main.name,
|
|
||||||
assetDtSymbol: ddo.dataTokenInfo.symbol
|
|
||||||
}
|
|
||||||
computeJobs.push(compJob)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setJobs(computeJobs)
|
|
||||||
} catch (error) {
|
|
||||||
Logger.log(error.message)
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
}
|
||||||
|
setJobs(computeJobs)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.log(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data === undefined || !config?.metadataCacheUri) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
getJobs()
|
getJobs()
|
||||||
}, [ocean, account, data, config?.metadataCacheUri])
|
}, [ocean, account, data, config?.metadataCacheUri])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<>
|
||||||
columns={columns}
|
{jobs.length > 0 && (
|
||||||
data={jobs}
|
<Button
|
||||||
isLoading={isLoading}
|
style="text"
|
||||||
defaultSortField="row.dateCreated"
|
size="small"
|
||||||
defaultSortAsc={false}
|
title="Refresh compute jobs"
|
||||||
/>
|
onClick={() => getJobs()}
|
||||||
|
disabled={isLoading}
|
||||||
|
className={styles.refresh}
|
||||||
|
>
|
||||||
|
<Refresh />
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={jobs}
|
||||||
|
isLoading={isLoading}
|
||||||
|
defaultSortField="row.dateCreated"
|
||||||
|
defaultSortAsc={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import Loader from '../../atoms/Loader'
|
|
||||||
import AssetList from '../../organisms/AssetList'
|
import AssetList from '../../organisms/AssetList'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { queryMetadata } from '../../../utils/aquarius'
|
import { queryMetadata } from '../../../utils/aquarius'
|
||||||
@ -48,14 +47,13 @@ export default function PublishedList(): ReactElement {
|
|||||||
getPublished()
|
getPublished()
|
||||||
}, [accountId, page, config.metadataCacheUri])
|
}, [accountId, page, config.metadataCacheUri])
|
||||||
|
|
||||||
return isLoading ? (
|
return accountId ? (
|
||||||
<Loader />
|
|
||||||
) : accountId && queryResult ? (
|
|
||||||
<AssetList
|
<AssetList
|
||||||
assets={queryResult.results}
|
assets={queryResult?.results}
|
||||||
|
isLoading={isLoading}
|
||||||
showPagination
|
showPagination
|
||||||
page={queryResult.page}
|
page={queryResult?.page}
|
||||||
totalPages={queryResult.totalPages}
|
totalPages={queryResult?.totalPages}
|
||||||
onPageChange={(newPage) => {
|
onPageChange={(newPage) => {
|
||||||
setPage(newPage)
|
setPage(newPage)
|
||||||
}}
|
}}
|
||||||
|
@ -16,9 +16,3 @@
|
|||||||
.section [class*='button'] {
|
.section [class*='button'] {
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaderWrap {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
@ -7,13 +7,13 @@ import {
|
|||||||
SearchQuery
|
SearchQuery
|
||||||
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import Container from '../atoms/Container'
|
import Container from '../atoms/Container'
|
||||||
import Loader from '../atoms/Loader'
|
|
||||||
import { useOcean } from '../../providers/Ocean'
|
import { useOcean } from '../../providers/Ocean'
|
||||||
import Button from '../atoms/Button'
|
import Button from '../atoms/Button'
|
||||||
import Bookmarks from '../molecules/Bookmarks'
|
import Bookmarks from '../molecules/Bookmarks'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { queryMetadata } from '../../utils/aquarius'
|
import { queryMetadata } from '../../utils/aquarius'
|
||||||
import Permission from '../organisms/Permission'
|
import Permission from '../organisms/Permission'
|
||||||
|
import { useWeb3 } from '../../providers/Web3'
|
||||||
|
|
||||||
const queryHighest = {
|
const queryHighest = {
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -37,14 +37,6 @@ const queryLatest = {
|
|||||||
sort: { created: -1 }
|
sort: { created: -1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoaderArea() {
|
|
||||||
return (
|
|
||||||
<div className={styles.loaderWrap}>
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SectionQueryResult({
|
function SectionQueryResult({
|
||||||
title,
|
title,
|
||||||
query,
|
query,
|
||||||
@ -56,11 +48,10 @@ function SectionQueryResult({
|
|||||||
}) {
|
}) {
|
||||||
const { config } = useOcean()
|
const { config } = useOcean()
|
||||||
const [result, setResult] = useState<QueryResult>()
|
const [result, setResult] = useState<QueryResult>()
|
||||||
const [loading, setLoading] = useState(true)
|
const { web3Loading } = useWeb3()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!config?.metadataCacheUri) return
|
if (!config?.metadataCacheUri || web3Loading) return
|
||||||
|
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
@ -70,23 +61,18 @@ function SectionQueryResult({
|
|||||||
source.token
|
source.token
|
||||||
)
|
)
|
||||||
setResult(result)
|
setResult(result)
|
||||||
setLoading(false)
|
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [config?.metadataCacheUri, query])
|
}, [config?.metadataCacheUri, query, web3Loading])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
{loading ? (
|
<AssetList assets={result?.results} showPagination={false} />
|
||||||
<LoaderArea />
|
|
||||||
) : (
|
|
||||||
result && <AssetList assets={result.results} showPagination={false} />
|
|
||||||
)}
|
|
||||||
{action && action}
|
{action && action}
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,6 @@ import Button from '../../atoms/Button'
|
|||||||
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||||
import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData'
|
import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData'
|
||||||
import { initialValues as initialValuesAlgorithm } from '../../../models/FormAlgoPublish'
|
import { initialValues as initialValuesAlgorithm } from '../../../models/FormAlgoPublish'
|
||||||
|
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
|
|
||||||
const query = graphql`
|
const query = graphql`
|
||||||
@ -63,6 +62,24 @@ export default function FormPublish(): ReactElement {
|
|||||||
initialValues.dockerImage
|
initialValues.dockerImage
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const dockerImageOptions = [
|
||||||
|
{
|
||||||
|
name: 'node:latest',
|
||||||
|
title: 'node:latest',
|
||||||
|
checked: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'python:latest',
|
||||||
|
title: 'python:latest',
|
||||||
|
checked: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'custom image',
|
||||||
|
title: 'custom image',
|
||||||
|
checked: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// reset form validation on every mount
|
// reset form validation on every mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setErrors({})
|
setErrors({})
|
||||||
@ -135,6 +152,11 @@ export default function FormPublish(): ReactElement {
|
|||||||
<Field
|
<Field
|
||||||
key={field.name}
|
key={field.name}
|
||||||
{...field}
|
{...field}
|
||||||
|
options={
|
||||||
|
field.type === 'boxSelection'
|
||||||
|
? dockerImageOptions
|
||||||
|
: field.options
|
||||||
|
}
|
||||||
component={Input}
|
component={Input}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
handleFieldChange(e, field)
|
handleFieldChange(e, field)
|
||||||
|
@ -7,6 +7,8 @@ import { FormContent, FormFieldProps } from '../../../@types/Form'
|
|||||||
import { MetadataPublishFormDataset } from '../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../@types/MetaData'
|
||||||
import { initialValues as initialValuesDataset } from '../../../models/FormAlgoPublish'
|
import { initialValues as initialValuesDataset } from '../../../models/FormAlgoPublish'
|
||||||
import { useOcean } from '../../../providers/Ocean'
|
import { useOcean } from '../../../providers/Ocean'
|
||||||
|
import { ReactComponent as Download } from '../../../images/download.svg'
|
||||||
|
import { ReactComponent as Compute } from '../../../images/compute.svg'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
import styles from './FormPublish.module.css'
|
import styles from './FormPublish.module.css'
|
||||||
|
|
||||||
@ -61,6 +63,19 @@ export default function FormPublish(): ReactElement {
|
|||||||
// setSubmitting(false)
|
// setSubmitting(false)
|
||||||
}, [setErrors, setTouched])
|
}, [setErrors, setTouched])
|
||||||
|
|
||||||
|
const accessTypeOptions = [
|
||||||
|
{
|
||||||
|
name: 'Download',
|
||||||
|
title: 'Download',
|
||||||
|
icon: <Download />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Compute',
|
||||||
|
title: 'Compute',
|
||||||
|
icon: <Compute />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// Manually handle change events instead of using `handleChange` from Formik.
|
// Manually handle change events instead of using `handleChange` from Formik.
|
||||||
// Workaround for default `validateOnChange` not kicking in
|
// Workaround for default `validateOnChange` not kicking in
|
||||||
function handleFieldChange(
|
function handleFieldChange(
|
||||||
@ -94,6 +109,9 @@ export default function FormPublish(): ReactElement {
|
|||||||
<Field
|
<Field
|
||||||
key={field.name}
|
key={field.name}
|
||||||
{...field}
|
{...field}
|
||||||
|
options={
|
||||||
|
field.type === 'boxSelection' ? accessTypeOptions : field.options
|
||||||
|
}
|
||||||
component={Input}
|
component={Input}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
handleFieldChange(e, field)
|
handleFieldChange(e, field)
|
||||||
|
@ -23,7 +23,7 @@ export default function PageTemplateAssetDetails({
|
|||||||
setPageTitle(isInPurgatory ? '' : title)
|
setPageTitle(isInPurgatory ? '' : title)
|
||||||
}, [ddo, error, isInPurgatory, title])
|
}, [ddo, error, isInPurgatory, title])
|
||||||
|
|
||||||
return ddo ? (
|
return ddo && pageTitle ? (
|
||||||
<>
|
<>
|
||||||
<Page title={pageTitle} uri={uri}>
|
<Page title={pageTitle} uri={uri}>
|
||||||
<Router basepath="/asset">
|
<Router basepath="/asset">
|
||||||
|
@ -1,188 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import { useNavigate } from '@reach/router'
|
|
||||||
import styles from './filterPrice.module.css'
|
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import {
|
|
||||||
addExistingParamsToUrl,
|
|
||||||
FilterByPriceOptions,
|
|
||||||
FilterByTypeOptions
|
|
||||||
} from './utils'
|
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
const clearFilters = [{ display: 'Clear', value: '' }]
|
|
||||||
|
|
||||||
const priceFilterItems = [
|
|
||||||
{ display: 'fixed price', value: FilterByPriceOptions.Fixed },
|
|
||||||
{ display: 'dynamic price', value: FilterByPriceOptions.Dynamic }
|
|
||||||
]
|
|
||||||
|
|
||||||
const serviceFilterItems = [
|
|
||||||
{ display: 'data sets', value: FilterByTypeOptions.Data },
|
|
||||||
{ display: 'algorithms', value: FilterByTypeOptions.Algorithm }
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function FilterPrice({
|
|
||||||
priceType,
|
|
||||||
serviceType,
|
|
||||||
setPriceType,
|
|
||||||
setServiceType
|
|
||||||
}: {
|
|
||||||
priceType: string
|
|
||||||
setPriceType: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
serviceType: string
|
|
||||||
setServiceType: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
}): ReactElement {
|
|
||||||
const navigate = useNavigate()
|
|
||||||
|
|
||||||
const [priceSelections, setPriceSelections] = useState<string[]>([])
|
|
||||||
const [serviceSelections, setServiceSelections] = useState<string[]>([])
|
|
||||||
|
|
||||||
async function applyPriceFilter(filterBy: string) {
|
|
||||||
let urlLocation = await addExistingParamsToUrl(location, 'priceType')
|
|
||||||
if (filterBy) {
|
|
||||||
urlLocation = `${urlLocation}&priceType=${filterBy}`
|
|
||||||
}
|
|
||||||
setPriceType(filterBy)
|
|
||||||
navigate(urlLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyServiceFilter(filterBy: string) {
|
|
||||||
let urlLocation = await addExistingParamsToUrl(location, 'serviceType')
|
|
||||||
if (filterBy && location.search.indexOf('&serviceType') === -1) {
|
|
||||||
urlLocation = `${urlLocation}&serviceType=${filterBy}`
|
|
||||||
}
|
|
||||||
setServiceType(filterBy)
|
|
||||||
navigate(urlLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSelectedFilter(isSelected: boolean, value: string) {
|
|
||||||
if (
|
|
||||||
value === FilterByPriceOptions.Fixed ||
|
|
||||||
value === FilterByPriceOptions.Dynamic
|
|
||||||
) {
|
|
||||||
if (isSelected) {
|
|
||||||
if (priceSelections.length > 1) {
|
|
||||||
// both selected -> select the other one
|
|
||||||
const otherValue = priceFilterItems.find((p) => p.value !== value)
|
|
||||||
.value
|
|
||||||
await applyPriceFilter(otherValue)
|
|
||||||
} else {
|
|
||||||
// only the current one selected -> deselect it
|
|
||||||
await applyPriceFilter(undefined)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (priceSelections.length > 0) {
|
|
||||||
// one already selected -> both selected
|
|
||||||
await applyPriceFilter(FilterByPriceOptions.All)
|
|
||||||
setPriceSelections(priceFilterItems.map((p) => p.value))
|
|
||||||
} else {
|
|
||||||
// none selected -> select
|
|
||||||
await applyPriceFilter(value)
|
|
||||||
setPriceSelections([value])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isSelected) {
|
|
||||||
if (serviceSelections.length > 1) {
|
|
||||||
const otherValue = serviceFilterItems.find((p) => p.value !== value)
|
|
||||||
.value
|
|
||||||
await applyServiceFilter(otherValue)
|
|
||||||
setServiceSelections([otherValue])
|
|
||||||
} else {
|
|
||||||
await applyServiceFilter(undefined)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (serviceSelections.length) {
|
|
||||||
await applyServiceFilter(undefined)
|
|
||||||
setServiceSelections(serviceFilterItems.map((p) => p.value))
|
|
||||||
} else {
|
|
||||||
await applyServiceFilter(value)
|
|
||||||
setServiceSelections([value])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyClearFilter() {
|
|
||||||
let urlLocation = await addExistingParamsToUrl(
|
|
||||||
location,
|
|
||||||
'priceType',
|
|
||||||
'serviceType'
|
|
||||||
)
|
|
||||||
|
|
||||||
urlLocation = `${urlLocation}`
|
|
||||||
|
|
||||||
setServiceSelections([])
|
|
||||||
setPriceSelections([])
|
|
||||||
|
|
||||||
setPriceType(undefined)
|
|
||||||
setServiceType(undefined)
|
|
||||||
navigate(urlLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.filterList}>
|
|
||||||
{priceFilterItems.map((e, index) => {
|
|
||||||
const isPriceSelected =
|
|
||||||
e.value === priceType || priceSelections.includes(e.value)
|
|
||||||
const selectFilter = cx({
|
|
||||||
[styles.selected]: isPriceSelected,
|
|
||||||
[styles.filter]: true
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
style="text"
|
|
||||||
key={index}
|
|
||||||
className={selectFilter}
|
|
||||||
onClick={async () => {
|
|
||||||
handleSelectedFilter(isPriceSelected, e.value)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e.display}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{serviceFilterItems.map((e, index) => {
|
|
||||||
const isServiceSelected =
|
|
||||||
e.value === serviceType || serviceSelections.includes(e.value)
|
|
||||||
const selectFilter = cx({
|
|
||||||
[styles.selected]: isServiceSelected,
|
|
||||||
[styles.filter]: true
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
style="text"
|
|
||||||
key={index}
|
|
||||||
className={selectFilter}
|
|
||||||
onClick={async () => {
|
|
||||||
handleSelectedFilter(isServiceSelected, e.value)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e.display}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{clearFilters.map((e, index) => {
|
|
||||||
const showClear =
|
|
||||||
priceSelections.length > 0 || serviceSelections.length > 0
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
style="text"
|
|
||||||
key={index}
|
|
||||||
className={showClear ? styles.showClear : styles.hideClear}
|
|
||||||
onClick={async () => {
|
|
||||||
applyClearFilter()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e.display}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -47,6 +47,10 @@ button.filter,
|
|||||||
margin-bottom: calc(var(--spacer) / 6);
|
margin-bottom: calc(var(--spacer) / 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.showClear:hover {
|
||||||
|
display: inline-flex;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
.showClear {
|
.showClear {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
111
src/components/templates/Search/filterService.tsx
Normal file
111
src/components/templates/Search/filterService.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import React, { ReactElement, useState } from 'react'
|
||||||
|
import { useNavigate } from '@reach/router'
|
||||||
|
import styles from './filterService.module.css'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import { addExistingParamsToUrl, FilterByTypeOptions } from './utils'
|
||||||
|
import Button from '../../atoms/Button'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
const clearFilters = [{ display: 'Clear', value: '' }]
|
||||||
|
|
||||||
|
const serviceFilterItems = [
|
||||||
|
{ display: 'data sets', value: FilterByTypeOptions.Data },
|
||||||
|
{ display: 'algorithms', value: FilterByTypeOptions.Algorithm }
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function FilterPrice({
|
||||||
|
serviceType,
|
||||||
|
setServiceType
|
||||||
|
}: {
|
||||||
|
serviceType: string
|
||||||
|
setServiceType: React.Dispatch<React.SetStateAction<string>>
|
||||||
|
}): ReactElement {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [serviceSelections, setServiceSelections] = useState<string[]>([])
|
||||||
|
|
||||||
|
async function applyServiceFilter(filterBy: string) {
|
||||||
|
let urlLocation = await addExistingParamsToUrl(location, 'serviceType')
|
||||||
|
if (filterBy && location.search.indexOf('&serviceType') === -1) {
|
||||||
|
urlLocation = `${urlLocation}&serviceType=${filterBy}`
|
||||||
|
}
|
||||||
|
setServiceType(filterBy)
|
||||||
|
navigate(urlLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSelectedFilter(isSelected: boolean, value: string) {
|
||||||
|
if (isSelected) {
|
||||||
|
if (serviceSelections.length > 1) {
|
||||||
|
const otherValue = serviceFilterItems.find((p) => p.value !== value)
|
||||||
|
.value
|
||||||
|
await applyServiceFilter(otherValue)
|
||||||
|
setServiceSelections([otherValue])
|
||||||
|
} else {
|
||||||
|
await applyServiceFilter(undefined)
|
||||||
|
if (serviceSelections.includes(value)) {
|
||||||
|
serviceSelections.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (serviceSelections.length) {
|
||||||
|
await applyServiceFilter(undefined)
|
||||||
|
setServiceSelections(serviceFilterItems.map((p) => p.value))
|
||||||
|
} else {
|
||||||
|
await applyServiceFilter(value)
|
||||||
|
setServiceSelections([value])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyClearFilter() {
|
||||||
|
let urlLocation = await addExistingParamsToUrl(location, 'serviceType')
|
||||||
|
|
||||||
|
urlLocation = `${urlLocation}`
|
||||||
|
|
||||||
|
setServiceSelections([])
|
||||||
|
setServiceType(undefined)
|
||||||
|
navigate(urlLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.filterList}>
|
||||||
|
{serviceFilterItems.map((e, index) => {
|
||||||
|
const isServiceSelected =
|
||||||
|
e.value === serviceType || serviceSelections.includes(e.value)
|
||||||
|
const selectFilter = cx({
|
||||||
|
[styles.selected]: isServiceSelected,
|
||||||
|
[styles.filter]: true
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
style="text"
|
||||||
|
key={index}
|
||||||
|
className={selectFilter}
|
||||||
|
onClick={async () => {
|
||||||
|
handleSelectedFilter(isServiceSelected, e.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{e.display}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
{clearFilters.map((e, index) => {
|
||||||
|
const showClear = serviceSelections.length > 0
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
style="text"
|
||||||
|
key={index}
|
||||||
|
className={showClear ? styles.showClear : styles.hideClear}
|
||||||
|
onClick={async () => {
|
||||||
|
applyClearFilter()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{e.display}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -5,7 +5,7 @@ import SearchBar from '../../molecules/SearchBar'
|
|||||||
import AssetList from '../../organisms/AssetList'
|
import AssetList from '../../organisms/AssetList'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import queryString from 'query-string'
|
import queryString from 'query-string'
|
||||||
import PriceFilter from './filterPrice'
|
import ServiceFilter from './filterService'
|
||||||
import Sort from './sort'
|
import Sort from './sort'
|
||||||
import { getResults } from './utils'
|
import { getResults } from './utils'
|
||||||
import { navigate } from 'gatsby'
|
import { navigate } from 'gatsby'
|
||||||
@ -22,19 +22,9 @@ export default function SearchPage({
|
|||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { config } = useOcean()
|
const { config } = useOcean()
|
||||||
const parsed = queryString.parse(location.search)
|
const parsed = queryString.parse(location.search)
|
||||||
const {
|
const { text, owner, tags, page, sort, sortOrder, serviceType } = parsed
|
||||||
text,
|
|
||||||
owner,
|
|
||||||
tags,
|
|
||||||
page,
|
|
||||||
sort,
|
|
||||||
sortOrder,
|
|
||||||
priceType,
|
|
||||||
serviceType
|
|
||||||
} = parsed
|
|
||||||
const [queryResult, setQueryResult] = useState<QueryResult>()
|
const [queryResult, setQueryResult] = useState<QueryResult>()
|
||||||
const [loading, setLoading] = useState<boolean>()
|
const [loading, setLoading] = useState<boolean>()
|
||||||
const [price, setPriceType] = useState<string>(priceType as string)
|
|
||||||
const [service, setServiceType] = useState<string>(serviceType as string)
|
const [service, setServiceType] = useState<string>(serviceType as string)
|
||||||
const [sortType, setSortType] = useState<string>(sort as string)
|
const [sortType, setSortType] = useState<string>(sort as string)
|
||||||
const [sortDirection, setSortDirection] = useState<string>(
|
const [sortDirection, setSortDirection] = useState<string>(
|
||||||
@ -59,7 +49,6 @@ export default function SearchPage({
|
|||||||
tags,
|
tags,
|
||||||
sort,
|
sort,
|
||||||
page,
|
page,
|
||||||
priceType,
|
|
||||||
serviceType,
|
serviceType,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
config.metadataCacheUri
|
config.metadataCacheUri
|
||||||
@ -82,10 +71,8 @@ export default function SearchPage({
|
|||||||
<SearchBar initialValue={(text || owner) as string} />
|
<SearchBar initialValue={(text || owner) as string} />
|
||||||
)}
|
)}
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<PriceFilter
|
<ServiceFilter
|
||||||
priceType={price}
|
|
||||||
serviceType={service}
|
serviceType={service}
|
||||||
setPriceType={setPriceType}
|
|
||||||
setServiceType={setServiceType}
|
setServiceType={setServiceType}
|
||||||
/>
|
/>
|
||||||
<Sort
|
<Sort
|
||||||
@ -93,25 +80,18 @@ export default function SearchPage({
|
|||||||
sortDirection={sortDirection}
|
sortDirection={sortDirection}
|
||||||
setSortType={setSortType}
|
setSortType={setSortType}
|
||||||
setSortDirection={setSortDirection}
|
setSortDirection={setSortDirection}
|
||||||
setPriceType={setPriceType}
|
|
||||||
setServiceType={setServiceType}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.results}>
|
<div className={styles.results}>
|
||||||
{loading ? (
|
<AssetList
|
||||||
<Loader />
|
assets={queryResult?.results}
|
||||||
) : queryResult ? (
|
showPagination
|
||||||
<AssetList
|
isLoading={loading}
|
||||||
assets={queryResult.results}
|
page={queryResult?.page}
|
||||||
showPagination
|
totalPages={queryResult?.totalPages}
|
||||||
page={queryResult.page}
|
onPageChange={setPage}
|
||||||
totalPages={queryResult.totalPages}
|
/>
|
||||||
onPageChange={setPage}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
</Permission>
|
</Permission>
|
||||||
|
@ -3,9 +3,7 @@ import { useNavigate } from '@reach/router'
|
|||||||
import {
|
import {
|
||||||
addExistingParamsToUrl,
|
addExistingParamsToUrl,
|
||||||
SortTermOptions,
|
SortTermOptions,
|
||||||
SortValueOptions,
|
SortValueOptions
|
||||||
FilterByPriceOptions,
|
|
||||||
FilterByTypeOptions
|
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
import styles from './sort.module.css'
|
import styles from './sort.module.css'
|
||||||
@ -13,26 +11,18 @@ import classNames from 'classnames/bind'
|
|||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
const sortItems = [
|
const sortItems = [{ display: 'Published', value: SortTermOptions.Created }]
|
||||||
{ display: 'Published', value: SortTermOptions.Created },
|
|
||||||
{ display: 'Liquidity', value: SortTermOptions.Liquidity },
|
|
||||||
{ display: 'Price', value: SortTermOptions.Price }
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function Sort({
|
export default function Sort({
|
||||||
sortType,
|
sortType,
|
||||||
setSortType,
|
setSortType,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
setSortDirection,
|
setSortDirection
|
||||||
setPriceType,
|
|
||||||
setServiceType
|
|
||||||
}: {
|
}: {
|
||||||
sortType: string
|
sortType: string
|
||||||
setSortType: React.Dispatch<React.SetStateAction<string>>
|
setSortType: React.Dispatch<React.SetStateAction<string>>
|
||||||
sortDirection: string
|
sortDirection: string
|
||||||
setSortDirection: React.Dispatch<React.SetStateAction<string>>
|
setSortDirection: React.Dispatch<React.SetStateAction<string>>
|
||||||
setPriceType: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
setServiceType: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const directionArrow = String.fromCharCode(
|
const directionArrow = String.fromCharCode(
|
||||||
@ -41,12 +31,7 @@ export default function Sort({
|
|||||||
async function sortResults(sortBy?: string, direction?: string) {
|
async function sortResults(sortBy?: string, direction?: string) {
|
||||||
let urlLocation: string
|
let urlLocation: string
|
||||||
if (sortBy) {
|
if (sortBy) {
|
||||||
urlLocation = await addExistingParamsToUrl(location, 'sort', 'priceType')
|
|
||||||
urlLocation = `${urlLocation}&sort=${sortBy}`
|
urlLocation = `${urlLocation}&sort=${sortBy}`
|
||||||
if (sortBy === SortTermOptions.Liquidity) {
|
|
||||||
urlLocation = `${urlLocation}&priceType=${FilterByPriceOptions.Dynamic}`
|
|
||||||
setPriceType(FilterByPriceOptions.Dynamic)
|
|
||||||
}
|
|
||||||
setSortType(sortBy)
|
setSortType(sortBy)
|
||||||
} else if (direction) {
|
} else if (direction) {
|
||||||
urlLocation = await addExistingParamsToUrl(location, 'sortOrder')
|
urlLocation = await addExistingParamsToUrl(location, 'sortOrder')
|
||||||
|
@ -6,8 +6,6 @@ import { MetadataCache, Logger } from '@oceanprotocol/lib'
|
|||||||
import queryString from 'query-string'
|
import queryString from 'query-string'
|
||||||
|
|
||||||
export const SortTermOptions = {
|
export const SortTermOptions = {
|
||||||
Liquidity: 'liquidity',
|
|
||||||
Price: 'price',
|
|
||||||
Created: 'created'
|
Created: 'created'
|
||||||
} as const
|
} as const
|
||||||
type SortTermOptions = typeof SortTermOptions[keyof typeof SortTermOptions]
|
type SortTermOptions = typeof SortTermOptions[keyof typeof SortTermOptions]
|
||||||
@ -25,36 +23,12 @@ export const SortValueOptions = {
|
|||||||
} as const
|
} as const
|
||||||
type SortValueOptions = typeof SortValueOptions[keyof typeof SortValueOptions]
|
type SortValueOptions = typeof SortValueOptions[keyof typeof SortValueOptions]
|
||||||
|
|
||||||
export const FilterByPriceOptions = {
|
|
||||||
Fixed: 'exchange',
|
|
||||||
Dynamic: 'pool',
|
|
||||||
All: 'all'
|
|
||||||
} as const
|
|
||||||
type FilterByPriceOptions = typeof FilterByPriceOptions[keyof typeof FilterByPriceOptions]
|
|
||||||
|
|
||||||
export const FilterByTypeOptions = {
|
export const FilterByTypeOptions = {
|
||||||
Data: 'dataset',
|
Data: 'dataset',
|
||||||
Algorithm: 'algorithm'
|
Algorithm: 'algorithm'
|
||||||
} as const
|
} as const
|
||||||
type FilterByTypeOptions = typeof FilterByTypeOptions[keyof typeof FilterByTypeOptions]
|
type FilterByTypeOptions = typeof FilterByTypeOptions[keyof typeof FilterByTypeOptions]
|
||||||
|
|
||||||
function addPriceFilterToQuery(sortTerm: string, priceFilter: string): string {
|
|
||||||
if (priceFilter === FilterByPriceOptions.All) {
|
|
||||||
sortTerm = priceFilter
|
|
||||||
? sortTerm === ''
|
|
||||||
? `(price.type:${FilterByPriceOptions.Fixed} OR price.type:${FilterByPriceOptions.Dynamic})`
|
|
||||||
: `${sortTerm} AND (price.type:${FilterByPriceOptions.Dynamic} OR price.type:${FilterByPriceOptions.Fixed})`
|
|
||||||
: sortTerm
|
|
||||||
} else {
|
|
||||||
sortTerm = priceFilter
|
|
||||||
? sortTerm === ''
|
|
||||||
? `price.type:${priceFilter}`
|
|
||||||
: `${sortTerm} AND price.type:${priceFilter}`
|
|
||||||
: sortTerm
|
|
||||||
}
|
|
||||||
return sortTerm
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTypeFilterToQuery(sortTerm: string, typeFilter: string): string {
|
function addTypeFilterToQuery(sortTerm: string, typeFilter: string): string {
|
||||||
sortTerm = typeFilter
|
sortTerm = typeFilter
|
||||||
? sortTerm === ''
|
? sortTerm === ''
|
||||||
@ -64,13 +38,8 @@ function addTypeFilterToQuery(sortTerm: string, typeFilter: string): string {
|
|||||||
return sortTerm
|
return sortTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortType(sortParam: string): string {
|
function getSortType(): string {
|
||||||
const sortTerm =
|
const sortTerm = SortTermOptions.Created
|
||||||
sortParam === SortTermOptions.Liquidity
|
|
||||||
? SortElasticTerm.Liquidity
|
|
||||||
: sortParam === SortTermOptions.Price
|
|
||||||
? SortElasticTerm.Price
|
|
||||||
: SortTermOptions.Created
|
|
||||||
return sortTerm
|
return sortTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +52,9 @@ export function getSearchQuery(
|
|||||||
offset?: string,
|
offset?: string,
|
||||||
sort?: string,
|
sort?: string,
|
||||||
sortOrder?: string,
|
sortOrder?: string,
|
||||||
priceType?: string,
|
|
||||||
serviceType?: string
|
serviceType?: string
|
||||||
): SearchQuery {
|
): SearchQuery {
|
||||||
const sortTerm = getSortType(sort)
|
const sortTerm = getSortType()
|
||||||
const sortValue = sortOrder === SortValueOptions.Ascending ? 1 : -1
|
const sortValue = sortOrder === SortValueOptions.Ascending ? 1 : -1
|
||||||
let searchTerm = owner
|
let searchTerm = owner
|
||||||
? `(publicKey.owner:${owner})`
|
? `(publicKey.owner:${owner})`
|
||||||
@ -97,15 +65,25 @@ export function getSearchQuery(
|
|||||||
? // eslint-disable-next-line no-useless-escape
|
? // eslint-disable-next-line no-useless-escape
|
||||||
`(service.attributes.additionalInformation.categories:\"${categories}\")`
|
`(service.attributes.additionalInformation.categories:\"${categories}\")`
|
||||||
: text || ''
|
: text || ''
|
||||||
|
|
||||||
|
// HACK: resolves the case sensitivity related to dataTokenInfo.symbol
|
||||||
|
searchTerm = '*' + searchTerm.toUpperCase() + '*'
|
||||||
searchTerm = addTypeFilterToQuery(searchTerm, serviceType)
|
searchTerm = addTypeFilterToQuery(searchTerm, serviceType)
|
||||||
searchTerm = addPriceFilterToQuery(searchTerm, priceType)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
page: Number(page) || 1,
|
page: Number(page) || 1,
|
||||||
offset: Number(offset) || 21,
|
offset: Number(offset) || 21,
|
||||||
query: {
|
query: {
|
||||||
query_string: {
|
query_string: {
|
||||||
query: `${searchTerm} -isInPurgatory:true`
|
query: `${searchTerm} -isInPurgatory:true`,
|
||||||
|
fields: [
|
||||||
|
'dataTokenInfo.name',
|
||||||
|
'dataTokenInfo.symbol',
|
||||||
|
'service.attributes.main.name',
|
||||||
|
'service.attributes.main.author',
|
||||||
|
'service.attributes.additionalInformation.description'
|
||||||
|
],
|
||||||
|
default_operator: 'AND'
|
||||||
}
|
}
|
||||||
// ...(owner && { 'publicKey.owner': [owner] }),
|
// ...(owner && { 'publicKey.owner': [owner] }),
|
||||||
// ...(tags && { tags: [tags] }),
|
// ...(tags && { tags: [tags] }),
|
||||||
@ -136,7 +114,6 @@ export async function getResults(
|
|||||||
offset?: string
|
offset?: string
|
||||||
sort?: string
|
sort?: string
|
||||||
sortOrder?: string
|
sortOrder?: string
|
||||||
priceType?: string
|
|
||||||
serviceType?: string
|
serviceType?: string
|
||||||
},
|
},
|
||||||
metadataCacheUri: string
|
metadataCacheUri: string
|
||||||
@ -150,10 +127,10 @@ export async function getResults(
|
|||||||
categories,
|
categories,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
priceType,
|
|
||||||
serviceType
|
serviceType
|
||||||
} = params
|
} = params
|
||||||
const metadataCache = new MetadataCache(metadataCacheUri, Logger)
|
const metadataCache = new MetadataCache(metadataCacheUri, Logger)
|
||||||
|
|
||||||
const searchQuery = getSearchQuery(
|
const searchQuery = getSearchQuery(
|
||||||
text,
|
text,
|
||||||
owner,
|
owner,
|
||||||
@ -163,11 +140,10 @@ export async function getResults(
|
|||||||
offset,
|
offset,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
priceType,
|
|
||||||
serviceType
|
serviceType
|
||||||
)
|
)
|
||||||
const queryResult = await metadataCache.queryMetadata(searchQuery)
|
|
||||||
|
|
||||||
|
const queryResult = await metadataCache.queryMetadata(searchQuery)
|
||||||
return queryResult
|
return queryResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,11 +12,9 @@ import { PurgatoryData } from '@oceanprotocol/lib/dist/node/ddo/interfaces/Purga
|
|||||||
import getAssetPurgatoryData from '../utils/purgatory'
|
import getAssetPurgatoryData from '../utils/purgatory'
|
||||||
import axios, { CancelToken } from 'axios'
|
import axios, { CancelToken } from 'axios'
|
||||||
import { retrieveDDO } from '../utils/aquarius'
|
import { retrieveDDO } from '../utils/aquarius'
|
||||||
|
import { getPrice } from '../utils/subgraph'
|
||||||
import { MetadataMarket } from '../@types/MetaData'
|
import { MetadataMarket } from '../@types/MetaData'
|
||||||
import { useOcean } from './Ocean'
|
import { useOcean } from './Ocean'
|
||||||
import { gql, useQuery } from '@apollo/client'
|
|
||||||
import { PoolPrice } from '../@types/apollo/PoolPrice'
|
|
||||||
import { FrePrice } from '../@types/apollo/FrePrice'
|
|
||||||
|
|
||||||
interface AssetProviderValue {
|
interface AssetProviderValue {
|
||||||
isInPurgatory: boolean
|
isInPurgatory: boolean
|
||||||
@ -33,26 +31,6 @@ interface AssetProviderValue {
|
|||||||
refreshDdo: (token?: CancelToken) => Promise<void>
|
refreshDdo: (token?: CancelToken) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const poolQuery = gql`
|
|
||||||
query PoolPrice($datatoken: String) {
|
|
||||||
pools(where: { datatokenAddress: $datatoken }) {
|
|
||||||
spotPrice
|
|
||||||
consumePrice
|
|
||||||
datatokenReserve
|
|
||||||
oceanReserve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const freQuery = gql`
|
|
||||||
query FrePrice($datatoken: String) {
|
|
||||||
fixedRateExchanges(orderBy: id, where: { datatoken: $datatoken }) {
|
|
||||||
rate
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const AssetContext = createContext({} as AssetProviderValue)
|
const AssetContext = createContext({} as AssetProviderValue)
|
||||||
|
|
||||||
const refreshInterval = 10000 // 10 sec.
|
const refreshInterval = 10000 // 10 sec.
|
||||||
@ -75,68 +53,6 @@ function AssetProvider({
|
|||||||
const [owner, setOwner] = useState<string>()
|
const [owner, setOwner] = useState<string>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const [type, setType] = useState<MetadataMain['type']>()
|
const [type, setType] = useState<MetadataMain['type']>()
|
||||||
const [variables, setVariables] = useState({})
|
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
const {
|
|
||||||
refetch: refetchFre,
|
|
||||||
startPolling: startPollingFre,
|
|
||||||
data: frePrice
|
|
||||||
} = useQuery<FrePrice>(freQuery, {
|
|
||||||
variables,
|
|
||||||
skip: false
|
|
||||||
})
|
|
||||||
const {
|
|
||||||
refetch: refetchPool,
|
|
||||||
startPolling: startPollingPool,
|
|
||||||
data: poolPrice
|
|
||||||
} = useQuery<PoolPrice>(poolQuery, {
|
|
||||||
variables,
|
|
||||||
skip: false
|
|
||||||
})
|
|
||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
||||||
|
|
||||||
// this is not working as expected, thus we need to fetch both pool and fre
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (!ddo || !variables || variables === '') return
|
|
||||||
|
|
||||||
// if (ddo.price.type === 'exchange') {
|
|
||||||
// refetchFre(variables)
|
|
||||||
// startPollingFre(refreshInterval)
|
|
||||||
// } else {
|
|
||||||
// refetchPool(variables)
|
|
||||||
// startPollingPool(refreshInterval)
|
|
||||||
// }
|
|
||||||
// }, [ddo, variables])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!frePrice ||
|
|
||||||
frePrice.fixedRateExchanges.length === 0 ||
|
|
||||||
price.type !== 'exchange'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
setPrice((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
value: frePrice.fixedRateExchanges[0].rate,
|
|
||||||
address: frePrice.fixedRateExchanges[0].id
|
|
||||||
}))
|
|
||||||
}, [frePrice])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!poolPrice || poolPrice.pools.length === 0 || price.type !== 'pool')
|
|
||||||
return
|
|
||||||
setPrice((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
value:
|
|
||||||
poolPrice.pools[0].consumePrice === '-1'
|
|
||||||
? poolPrice.pools[0].spotPrice
|
|
||||||
: poolPrice.pools[0].consumePrice,
|
|
||||||
ocean: poolPrice.pools[0].oceanReserve,
|
|
||||||
datatoken: poolPrice.pools[0].datatokenReserve,
|
|
||||||
isConsumable: poolPrice.pools[0].consumePrice === '-1' ? 'false' : 'true'
|
|
||||||
}))
|
|
||||||
}, [poolPrice])
|
|
||||||
|
|
||||||
const fetchDdo = async (token?: CancelToken) => {
|
const fetchDdo = async (token?: CancelToken) => {
|
||||||
Logger.log('[asset] Init asset, get DDO')
|
Logger.log('[asset] Init asset, get DDO')
|
||||||
@ -201,10 +117,8 @@ function AssetProvider({
|
|||||||
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
||||||
if (!ddo) return
|
if (!ddo) return
|
||||||
|
|
||||||
// Set price & metadata from DDO first
|
const returnedPrice = await getPrice(ddo)
|
||||||
// TODO Hacky hack, temporary™: set isConsumable to true by default since Aquarius can't be trusted.
|
setPrice({ ...returnedPrice })
|
||||||
setPrice({ ...ddo.price, isConsumable: 'true' })
|
|
||||||
setVariables({ datatoken: ddo?.dataToken.toLowerCase() })
|
|
||||||
|
|
||||||
// Get metadata from DDO
|
// Get metadata from DDO
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
|
@ -8,7 +8,7 @@ import React, {
|
|||||||
useCallback
|
useCallback
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import Web3Modal from 'web3modal'
|
import Web3Modal, { getProviderInfo, IProviderInfo } from 'web3modal'
|
||||||
import { infuraProjectId as infuraId, portisId } from '../../app.config'
|
import { infuraProjectId as infuraId, portisId } from '../../app.config'
|
||||||
import WalletConnectProvider from '@walletconnect/web3-provider'
|
import WalletConnectProvider from '@walletconnect/web3-provider'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
@ -24,6 +24,7 @@ interface Web3ProviderValue {
|
|||||||
web3: Web3
|
web3: Web3
|
||||||
web3Provider: any
|
web3Provider: any
|
||||||
web3Modal: Web3Modal
|
web3Modal: Web3Modal
|
||||||
|
web3ProviderInfo: IProviderInfo
|
||||||
accountId: string
|
accountId: string
|
||||||
networkId: number
|
networkId: number
|
||||||
networkDisplayName: string
|
networkDisplayName: string
|
||||||
@ -106,6 +107,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
const [web3, setWeb3] = useState<Web3>()
|
const [web3, setWeb3] = useState<Web3>()
|
||||||
const [web3Provider, setWeb3Provider] = useState<any>()
|
const [web3Provider, setWeb3Provider] = useState<any>()
|
||||||
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
|
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
|
||||||
|
const [web3ProviderInfo, setWeb3ProviderInfo] = useState<IProviderInfo>()
|
||||||
const [networkId, setNetworkId] = useState<number>()
|
const [networkId, setNetworkId] = useState<number>()
|
||||||
const [networkDisplayName, setNetworkDisplayName] = useState<string>()
|
const [networkDisplayName, setNetworkDisplayName] = useState<string>()
|
||||||
const [networkData, setNetworkData] = useState<EthereumListsChain>()
|
const [networkData, setNetworkData] = useState<EthereumListsChain>()
|
||||||
@ -148,7 +150,10 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
// Create initial Web3Modal instance
|
// Create initial Web3Modal instance
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (web3Modal) return
|
if (web3Modal) {
|
||||||
|
setWeb3Loading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
// note: needs artificial await here so the log message is reached and output
|
// note: needs artificial await here so the log message is reached and output
|
||||||
@ -209,6 +214,18 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
getBlock()
|
getBlock()
|
||||||
}, [web3, networkId])
|
}, [web3, networkId])
|
||||||
|
|
||||||
|
// -----------------------------------
|
||||||
|
// Get and set web3 provider info
|
||||||
|
// -----------------------------------
|
||||||
|
// Workaround cause getInjectedProviderName() always returns `MetaMask`
|
||||||
|
// https://github.com/oceanprotocol/market/issues/332
|
||||||
|
useEffect(() => {
|
||||||
|
if (!web3Provider) return
|
||||||
|
|
||||||
|
const providerInfo = getProviderInfo(web3Provider)
|
||||||
|
setWeb3ProviderInfo(providerInfo)
|
||||||
|
}, [web3Provider])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Logout helper
|
// Logout helper
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -255,6 +272,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
web3,
|
web3,
|
||||||
web3Provider,
|
web3Provider,
|
||||||
web3Modal,
|
web3Modal,
|
||||||
|
web3ProviderInfo,
|
||||||
accountId,
|
accountId,
|
||||||
networkId,
|
networkId,
|
||||||
networkDisplayName,
|
networkDisplayName,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
SearchQuery
|
SearchQuery
|
||||||
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
||||||
import { PriceList, getAssetPrices } from './subgraph'
|
import { PriceList, getAssetsPriceList } from './subgraph'
|
||||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||||
|
|
||||||
// TODO: import directly from ocean.js somehow.
|
// TODO: import directly from ocean.js somehow.
|
||||||
@ -113,7 +113,7 @@ export async function transformDDOToAssetSelection(
|
|||||||
): Promise<AssetSelectionAsset[]> {
|
): Promise<AssetSelectionAsset[]> {
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
const didList: string[] = []
|
const didList: string[] = []
|
||||||
const priceList: PriceList = await getAssetPrices(ddoList)
|
const priceList: PriceList = await getAssetsPriceList(ddoList)
|
||||||
const symbolList: any = {}
|
const symbolList: any = {}
|
||||||
for (const ddo of ddoList) {
|
for (const ddo of ddoList) {
|
||||||
didList.push(ddo.id)
|
didList.push(ddo.id)
|
||||||
|
@ -1,14 +1,32 @@
|
|||||||
import { gql, DocumentNode, ApolloQueryResult } from '@apollo/client'
|
import { gql, DocumentNode, ApolloQueryResult } from '@apollo/client'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO, BestPrice } from '@oceanprotocol/lib'
|
||||||
import { getApolloClientInstance } from '../providers/ApolloClientProvider'
|
import { getApolloClientInstance } from '../providers/ApolloClientProvider'
|
||||||
|
import {
|
||||||
|
AssetsPoolPrice,
|
||||||
|
AssetsPoolPrice_pools as AssetsPoolPricePools
|
||||||
|
} from '../@types/apollo/AssetsPoolPrice'
|
||||||
|
import {
|
||||||
|
AssetsFrePrice,
|
||||||
|
AssetsFrePrice_fixedRateExchanges as AssetsFrePriceFixedRateExchanges
|
||||||
|
} from '../@types/apollo/AssetsFrePrice'
|
||||||
|
import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
|
||||||
export interface PriceList {
|
export interface PriceList {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const freQuery = gql`
|
export interface AssetListPrices {
|
||||||
query AssetFrePrice($datatoken_in: [String!]) {
|
ddo: DDO
|
||||||
|
price: BestPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DidAndDatatokenMap {
|
||||||
|
[name: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FreQuery = gql`
|
||||||
|
query AssetsFrePrice($datatoken_in: [String!]) {
|
||||||
fixedRateExchanges(orderBy: id, where: { datatoken_in: $datatoken_in }) {
|
fixedRateExchanges(orderBy: id, where: { datatoken_in: $datatoken_in }) {
|
||||||
rate
|
rate
|
||||||
id
|
id
|
||||||
@ -20,18 +38,42 @@ const freQuery = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const poolQuery = gql`
|
const AssetFreQuery = gql`
|
||||||
query AssetPoolPrice($datatokenAddress_in: [String!]) {
|
query AssetFrePrice($datatoken: String) {
|
||||||
pools(where: { datatokenAddress_in: $datatokenAddress_in }) {
|
fixedRateExchanges(orderBy: id, where: { datatoken: $datatoken }) {
|
||||||
spotPrice
|
rate
|
||||||
consumePrice
|
|
||||||
id
|
id
|
||||||
datatokenAddress
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const previousOrderQuery = gql`
|
const PoolQuery = gql`
|
||||||
|
query AssetsPoolPrice($datatokenAddress_in: [String!]) {
|
||||||
|
pools(where: { datatokenAddress_in: $datatokenAddress_in }) {
|
||||||
|
id
|
||||||
|
spotPrice
|
||||||
|
consumePrice
|
||||||
|
datatokenAddress
|
||||||
|
datatokenReserve
|
||||||
|
oceanReserve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssetPoolPriceQuerry = gql`
|
||||||
|
query AssetPoolPrice($datatokenAddress: String) {
|
||||||
|
pools(where: { datatokenAddress: $datatokenAddress }) {
|
||||||
|
id
|
||||||
|
spotPrice
|
||||||
|
consumePrice
|
||||||
|
datatokenAddress
|
||||||
|
datatokenReserve
|
||||||
|
oceanReserve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const PreviousOrderQuery = gql`
|
||||||
query AssetPreviousOrder($id: String!, $account: String!) {
|
query AssetPreviousOrder($id: String!, $account: String!) {
|
||||||
tokenOrders(
|
tokenOrders(
|
||||||
first: 1
|
first: 1
|
||||||
@ -53,7 +95,8 @@ async function fetchData(
|
|||||||
const client = getApolloClientInstance()
|
const client = getApolloClientInstance()
|
||||||
const response = await client.query({
|
const response = await client.query({
|
||||||
query: query,
|
query: query,
|
||||||
variables: variables
|
variables: variables,
|
||||||
|
fetchPolicy: 'no-cache'
|
||||||
})
|
})
|
||||||
return response
|
return response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -70,19 +113,18 @@ export async function getPreviousOrders(
|
|||||||
id: id,
|
id: id,
|
||||||
account: account
|
account: account
|
||||||
}
|
}
|
||||||
const fetchedPreviousOrders: any = await fetchData(
|
const fetchedPreviousOrders: ApolloQueryResult<AssetPreviousOrder> = await fetchData(
|
||||||
previousOrderQuery,
|
PreviousOrderQuery,
|
||||||
variables
|
variables
|
||||||
)
|
)
|
||||||
if (fetchedPreviousOrders.data?.tokenOrders?.length === 0) return null
|
if (fetchedPreviousOrders.data?.tokenOrders?.length === 0) return null
|
||||||
if (assetTimeout === '0') {
|
if (assetTimeout === '0') {
|
||||||
return fetchedPreviousOrders?.data?.tokenOrders[0]?.tx
|
return fetchedPreviousOrders?.data?.tokenOrders[0]?.tx
|
||||||
} else {
|
} else {
|
||||||
const expiry = new BigNumber(
|
const expiry =
|
||||||
fetchedPreviousOrders?.data?.tokenOrders[0]?.timestamp
|
fetchedPreviousOrders?.data?.tokenOrders[0]?.timestamp * 1000 +
|
||||||
).plus(assetTimeout)
|
Number(assetTimeout) * 1000
|
||||||
const unixTime = new BigNumber(Math.floor(Date.now() / 1000))
|
if (Date.now() <= expiry) {
|
||||||
if (unixTime.isLessThan(expiry)) {
|
|
||||||
return fetchedPreviousOrders?.data?.tokenOrders[0]?.tx
|
return fetchedPreviousOrders?.data?.tokenOrders[0]?.tx
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
@ -90,9 +132,63 @@ export async function getPreviousOrders(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAssetPrices(assets: DDO[]): Promise<PriceList> {
|
function transformPriceToBestPrice(
|
||||||
const priceList: PriceList = {}
|
frePrice: AssetsFrePriceFixedRateExchanges[],
|
||||||
const didDTMap: any = {}
|
poolPrice: AssetsPoolPricePools[]
|
||||||
|
) {
|
||||||
|
if (poolPrice?.length > 0) {
|
||||||
|
const price: BestPrice = {
|
||||||
|
type: 'pool',
|
||||||
|
address: poolPrice[0]?.id,
|
||||||
|
value:
|
||||||
|
poolPrice[0]?.consumePrice === '-1'
|
||||||
|
? poolPrice[0]?.spotPrice
|
||||||
|
: poolPrice[0]?.consumePrice,
|
||||||
|
ocean: poolPrice[0]?.oceanReserve,
|
||||||
|
datatoken: poolPrice[0]?.datatokenReserve,
|
||||||
|
pools: [poolPrice[0]?.id],
|
||||||
|
isConsumable: poolPrice[0]?.consumePrice === '-1' ? 'false' : 'true'
|
||||||
|
}
|
||||||
|
return price
|
||||||
|
} else if (frePrice?.length > 0) {
|
||||||
|
// TODO Hacky hack, temporary™: set isConsumable to true for fre assets.
|
||||||
|
// isConsumable: 'true'
|
||||||
|
const price: BestPrice = {
|
||||||
|
type: 'exchange',
|
||||||
|
value: frePrice[0]?.rate,
|
||||||
|
address: frePrice[0]?.id,
|
||||||
|
exchange_id: frePrice[0]?.id,
|
||||||
|
ocean: 0,
|
||||||
|
datatoken: 0,
|
||||||
|
pools: [],
|
||||||
|
isConsumable: 'true'
|
||||||
|
}
|
||||||
|
return price
|
||||||
|
} else {
|
||||||
|
const price: BestPrice = {
|
||||||
|
type: '',
|
||||||
|
value: 0,
|
||||||
|
address: '',
|
||||||
|
exchange_id: '',
|
||||||
|
ocean: 0,
|
||||||
|
datatoken: 0,
|
||||||
|
pools: [],
|
||||||
|
isConsumable: 'false'
|
||||||
|
}
|
||||||
|
return price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAssetsPoolsExchangesAndDatatokenMap(
|
||||||
|
assets: DDO[]
|
||||||
|
): Promise<
|
||||||
|
[
|
||||||
|
ApolloQueryResult<AssetsPoolPrice>,
|
||||||
|
ApolloQueryResult<AssetsFrePrice>,
|
||||||
|
DidAndDatatokenMap
|
||||||
|
]
|
||||||
|
> {
|
||||||
|
const didDTMap: DidAndDatatokenMap = {}
|
||||||
const dataTokenList: string[] = []
|
const dataTokenList: string[] = []
|
||||||
|
|
||||||
for (const ddo of assets) {
|
for (const ddo of assets) {
|
||||||
@ -105,16 +201,100 @@ export async function getAssetPrices(assets: DDO[]): Promise<PriceList> {
|
|||||||
const poolVariables = {
|
const poolVariables = {
|
||||||
datatokenAddress_in: dataTokenList
|
datatokenAddress_in: dataTokenList
|
||||||
}
|
}
|
||||||
const poolPriceResponse: any = await fetchData(poolQuery, poolVariables)
|
|
||||||
|
const poolPriceResponse: ApolloQueryResult<AssetsPoolPrice> = await fetchData(
|
||||||
|
PoolQuery,
|
||||||
|
poolVariables
|
||||||
|
)
|
||||||
|
const frePriceResponse: ApolloQueryResult<AssetsFrePrice> = await fetchData(
|
||||||
|
FreQuery,
|
||||||
|
freVariables
|
||||||
|
)
|
||||||
|
|
||||||
|
return [poolPriceResponse, frePriceResponse, didDTMap]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> {
|
||||||
|
const priceList: PriceList = {}
|
||||||
|
|
||||||
|
const values: [
|
||||||
|
ApolloQueryResult<AssetsPoolPrice>,
|
||||||
|
ApolloQueryResult<AssetsFrePrice>,
|
||||||
|
DidAndDatatokenMap
|
||||||
|
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
||||||
|
const poolPriceResponse = values[0]
|
||||||
|
const frePriceResponse = values[1]
|
||||||
|
const didDTMap: DidAndDatatokenMap = values[2]
|
||||||
|
|
||||||
for (const poolPrice of poolPriceResponse.data?.pools) {
|
for (const poolPrice of poolPriceResponse.data?.pools) {
|
||||||
priceList[didDTMap[poolPrice.datatokenAddress]] =
|
priceList[didDTMap[poolPrice.datatokenAddress]] =
|
||||||
poolPrice.consumePrice === '-1'
|
poolPrice.consumePrice === '-1'
|
||||||
? poolPrice.spotPrice
|
? poolPrice.spotPrice
|
||||||
: poolPrice.consumePrice
|
: poolPrice.consumePrice
|
||||||
}
|
}
|
||||||
const frePriceResponse: any = await fetchData(freQuery, freVariables)
|
|
||||||
for (const frePrice of frePriceResponse.data?.fixedRateExchanges) {
|
for (const frePrice of frePriceResponse.data?.fixedRateExchanges) {
|
||||||
priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.rate
|
priceList[didDTMap[frePrice.datatoken?.address]] = frePrice.rate
|
||||||
}
|
}
|
||||||
return priceList
|
return priceList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPrice(asset: DDO): Promise<BestPrice> {
|
||||||
|
const freVariables = {
|
||||||
|
datatoken: asset?.dataToken.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
const poolVariables = {
|
||||||
|
datatokenAddress: asset?.dataToken.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
const poolPriceResponse: ApolloQueryResult<AssetsPoolPrice> = await fetchData(
|
||||||
|
AssetPoolPriceQuerry,
|
||||||
|
poolVariables
|
||||||
|
)
|
||||||
|
const frePriceResponse: ApolloQueryResult<AssetsFrePrice> = await fetchData(
|
||||||
|
AssetFreQuery,
|
||||||
|
freVariables
|
||||||
|
)
|
||||||
|
|
||||||
|
const bestPrice: BestPrice = transformPriceToBestPrice(
|
||||||
|
frePriceResponse.data.fixedRateExchanges,
|
||||||
|
poolPriceResponse.data.pools
|
||||||
|
)
|
||||||
|
|
||||||
|
return bestPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAssetsBestPrices(
|
||||||
|
assets: DDO[]
|
||||||
|
): Promise<AssetListPrices[]> {
|
||||||
|
const assetsWithPrice: AssetListPrices[] = []
|
||||||
|
|
||||||
|
const values: [
|
||||||
|
ApolloQueryResult<AssetsPoolPrice>,
|
||||||
|
ApolloQueryResult<AssetsFrePrice>,
|
||||||
|
DidAndDatatokenMap
|
||||||
|
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
||||||
|
const poolPriceResponse = values[0]
|
||||||
|
const frePriceResponse = values[1]
|
||||||
|
|
||||||
|
for (const ddo of assets) {
|
||||||
|
const dataToken = ddo.dataToken.toLowerCase()
|
||||||
|
const poolPrice: AssetsPoolPricePools[] = []
|
||||||
|
const frePrice: AssetsFrePriceFixedRateExchanges[] = []
|
||||||
|
const pool = poolPriceResponse.data?.pools.find(
|
||||||
|
(pool: any) => pool.datatokenAddress === dataToken
|
||||||
|
)
|
||||||
|
pool && poolPrice.push(pool)
|
||||||
|
const fre = frePriceResponse.data?.fixedRateExchanges.find(
|
||||||
|
(fre: any) => fre.datatoken.address === dataToken
|
||||||
|
)
|
||||||
|
fre && frePrice.push(fre)
|
||||||
|
const bestPrice = transformPriceToBestPrice(frePrice, poolPrice)
|
||||||
|
assetsWithPrice.push({
|
||||||
|
ddo: ddo,
|
||||||
|
price: bestPrice
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetsWithPrice
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Logger, ConfigHelperConfig } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export interface EthereumListsChain {
|
export interface EthereumListsChain {
|
||||||
name: string
|
name: string
|
||||||
@ -79,32 +79,33 @@ export function addCustomNetwork(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addOceanToWallet(
|
export async function addTokenToWallet(
|
||||||
config: ConfigHelperConfig,
|
web3Provider: any,
|
||||||
web3Provider: any
|
address: string,
|
||||||
): void {
|
symbol: string,
|
||||||
|
logo?: string
|
||||||
|
): Promise<void> {
|
||||||
|
const image =
|
||||||
|
logo ||
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png'
|
||||||
|
|
||||||
const tokenMetadata = {
|
const tokenMetadata = {
|
||||||
type: 'ERC20',
|
type: 'ERC20',
|
||||||
options: {
|
options: { address, symbol, image, decimals: 18 }
|
||||||
address: config.oceanTokenAddress,
|
|
||||||
symbol: config.oceanTokenSymbol,
|
|
||||||
decimals: 18,
|
|
||||||
image:
|
|
||||||
'https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
web3Provider.sendAsync(
|
web3Provider.sendAsync(
|
||||||
{
|
{
|
||||||
method: 'wallet_watchAsset',
|
method: 'wallet_watchAsset',
|
||||||
params: tokenMetadata,
|
params: tokenMetadata,
|
||||||
id: Math.round(Math.random() * 100000)
|
id: Math.round(Math.random() * 100000)
|
||||||
},
|
},
|
||||||
(err: string, added: any) => {
|
(err: { code: number; message: string }, added: any) => {
|
||||||
if (err || 'error' in added) {
|
if (err || 'error' in added) {
|
||||||
Logger.error(
|
Logger.error(
|
||||||
`Couldn't add ${tokenMetadata.options.symbol} (${
|
`Couldn't add ${tokenMetadata.options.symbol} (${
|
||||||
tokenMetadata.options.address
|
tokenMetadata.options.address
|
||||||
}) to MetaMask, error: ${err || added.error}`
|
}) to MetaMask, error: ${err.message || added.error}`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Logger.log(
|
Logger.log(
|
||||||
|
Loading…
Reference in New Issue
Block a user