mirror of
https://github.com/oceanprotocol/market.git
synced 2024-11-15 01:34:57 +01:00
Merge branch 'main' into feature/history-compute
Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro>
This commit is contained in:
commit
67cd26258c
@ -42,9 +42,9 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "price",
|
"name": "dataTokenOptions",
|
||||||
"label": "Price",
|
"label": "Datatoken",
|
||||||
"type": "price",
|
"type": "datatoken",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -96,21 +96,5 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"success": "Asset Created!"
|
"success": "Asset Created!"
|
||||||
},
|
|
||||||
"price": {
|
|
||||||
"fixed": {
|
|
||||||
"title": "Fixed",
|
|
||||||
"info": "Set your price for accessing this data set. A Datatoken for this data set, worth the entered amount of OCEAN, will be created."
|
|
||||||
},
|
|
||||||
"dynamic": {
|
|
||||||
"title": "Dynamic",
|
|
||||||
"info": "Let's create a decentralized, automated market for your data set. A Datatoken for this data set, worth the entered amount of OCEAN, will be created. Additionally, you will provide liquidity into a OCEAN/Datatoken liquidity pool with Balancer.",
|
|
||||||
"tooltips": {
|
|
||||||
"poolInfo": "Explain what is going on here...",
|
|
||||||
"swapFee": "Explain liquidity provider fee...",
|
|
||||||
"communityFee": "Explain community fee...",
|
|
||||||
"marketplaceFee": "Explain marketplace fee..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,28 @@
|
|||||||
{
|
{
|
||||||
|
"create": {
|
||||||
|
"empty": {
|
||||||
|
"title": "No Price Created",
|
||||||
|
"info": "This data set has no price yet. As the publisher you can create a fixed price, or a dynamic price for it. Onwards!",
|
||||||
|
"action": {
|
||||||
|
"name": "Create Pricing",
|
||||||
|
"help": "Create Pricing will mint your datatokens, approve spending, and create either a pool or a fixed rate exchange in one process. You will need to approve those multiple steps in your wallet."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fixed": {
|
||||||
|
"title": "Fixed",
|
||||||
|
"info": "Set your price for accessing this data set. The datatoken for this data set will be worth the entered amount of OCEAN."
|
||||||
|
},
|
||||||
|
"dynamic": {
|
||||||
|
"title": "Dynamic",
|
||||||
|
"info": "Let's create a decentralized, automated market for your data set. The datatoken for this data set will be worth the entered amount of OCEAN. Additionally, you will provide liquidity into a Datatoken/OCEAN liquidity pool with Balancer.",
|
||||||
|
"tooltips": {
|
||||||
|
"poolInfo": "Explain what is going on here...",
|
||||||
|
"swapFee": "Explain liquidity provider fee...",
|
||||||
|
"communityFee": "Explain community fee...",
|
||||||
|
"marketplaceFee": "Explain marketplace fee..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"pool": {
|
"pool": {
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
"price": "Explain how this price is determined...",
|
"price": "Explain how this price is determined...",
|
||||||
|
1642
package-lock.json
generated
1642
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@ -22,8 +22,13 @@
|
|||||||
"@coingecko/cryptoformat": "^0.4.2",
|
"@coingecko/cryptoformat": "^0.4.2",
|
||||||
"@loadable/component": "5.13.1",
|
"@loadable/component": "5.13.1",
|
||||||
"@oceanprotocol/art": "^3.0.0",
|
"@oceanprotocol/art": "^3.0.0",
|
||||||
|
<<<<<<< HEAD
|
||||||
"@oceanprotocol/lib": "^0.6.7",
|
"@oceanprotocol/lib": "^0.6.7",
|
||||||
"@oceanprotocol/react": "^0.2.2",
|
"@oceanprotocol/react": "^0.2.2",
|
||||||
|
=======
|
||||||
|
"@oceanprotocol/lib": "^0.7.1",
|
||||||
|
"@oceanprotocol/react": "^0.3.2",
|
||||||
|
>>>>>>> main
|
||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@sindresorhus/slugify": "^1.0.0",
|
"@sindresorhus/slugify": "^1.0.0",
|
||||||
"@tippyjs/react": "^4.2.0",
|
"@tippyjs/react": "^4.2.0",
|
||||||
@ -40,19 +45,19 @@
|
|||||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
||||||
"filesize": "^6.1.0",
|
"filesize": "^6.1.0",
|
||||||
"formik": "^2.2.0",
|
"formik": "^2.2.0",
|
||||||
"gatsby": "^2.24.80",
|
"gatsby": "^2.24.84",
|
||||||
"gatsby-image": "^2.4.21",
|
"gatsby-image": "^2.4.21",
|
||||||
"gatsby-plugin-manifest": "^2.4.35",
|
"gatsby-plugin-manifest": "^2.4.35",
|
||||||
"gatsby-plugin-react-helmet": "^3.3.14",
|
"gatsby-plugin-react-helmet": "^3.3.14",
|
||||||
"gatsby-plugin-remove-trailing-slashes": "^2.3.13",
|
"gatsby-plugin-remove-trailing-slashes": "^2.3.13",
|
||||||
"gatsby-plugin-sharp": "^2.6.42",
|
"gatsby-plugin-sharp": "^2.6.43",
|
||||||
"gatsby-plugin-svgr": "^2.0.2",
|
"gatsby-plugin-svgr": "^2.0.2",
|
||||||
"gatsby-plugin-webpack-size": "^1.0.0",
|
"gatsby-plugin-webpack-size": "^1.0.0",
|
||||||
"gatsby-source-filesystem": "^2.3.35",
|
"gatsby-source-filesystem": "^2.3.35",
|
||||||
"gatsby-source-graphql": "^2.7.6",
|
"gatsby-source-graphql": "^2.7.6",
|
||||||
"gatsby-transformer-json": "^2.4.14",
|
"gatsby-transformer-json": "^2.4.15",
|
||||||
"gatsby-transformer-remark": "^2.8.42",
|
"gatsby-transformer-remark": "^2.8.45",
|
||||||
"gatsby-transformer-sharp": "^2.5.18",
|
"gatsby-transformer-sharp": "^2.5.19",
|
||||||
"intersection-observer": "^0.11.0",
|
"intersection-observer": "^0.11.0",
|
||||||
"is-url-superb": "^4.0.0",
|
"is-url-superb": "^4.0.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
@ -74,7 +79,7 @@
|
|||||||
"remove-markdown": "^0.3.0",
|
"remove-markdown": "^0.3.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"slugify": "^1.4.5",
|
"slugify": "^1.4.5",
|
||||||
"swr": "^0.3.5",
|
"swr": "^0.3.6",
|
||||||
"yup": "^0.29.3"
|
"yup": "^0.29.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -86,11 +91,11 @@
|
|||||||
"@svgr/webpack": "^5.4.0",
|
"@svgr/webpack": "^5.4.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@types/jest": "^26.0.14",
|
"@types/jest": "^26.0.15",
|
||||||
"@types/loadable__component": "^5.13.1",
|
"@types/loadable__component": "^5.13.1",
|
||||||
"@types/lodash.debounce": "^4.0.3",
|
"@types/lodash.debounce": "^4.0.3",
|
||||||
"@types/lodash.omit": "^4.5.6",
|
"@types/lodash.omit": "^4.5.6",
|
||||||
"@types/node": "^14.11.10",
|
"@types/node": "^14.14.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-datepicker": "^3.1.1",
|
"@types/react-datepicker": "^3.1.1",
|
||||||
"@types/react-helmet": "^6.1.0",
|
"@types/react-helmet": "^6.1.0",
|
||||||
@ -99,18 +104,18 @@
|
|||||||
"@types/remove-markdown": "^0.1.1",
|
"@types/remove-markdown": "^0.1.1",
|
||||||
"@types/shortid": "0.0.29",
|
"@types/shortid": "0.0.29",
|
||||||
"@types/yup": "^0.29.8",
|
"@types/yup": "^0.29.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.4.1",
|
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
||||||
"@typescript-eslint/parser": "^4.4.1",
|
"@typescript-eslint/parser": "^4.5.0",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-preset-react-app": "^9.1.2",
|
"babel-preset-react-app": "^9.1.2",
|
||||||
"eslint": "^7.11.0",
|
"eslint": "^7.11.0",
|
||||||
"eslint-config-oceanprotocol": "^1.5.0",
|
"eslint-config-oceanprotocol": "^1.5.0",
|
||||||
"eslint-config-prettier": "^6.13.0",
|
"eslint-config-prettier": "^6.13.0",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"eslint-plugin-react": "^7.21.4",
|
"eslint-plugin-react": "^7.21.5",
|
||||||
"eslint-plugin-react-hooks": "^4.1.2",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^26.5.3",
|
"jest": "^26.6.0",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
"serve": "^11.3.2",
|
"serve": "^11.3.2",
|
||||||
"source-map-explorer": "^2.5.0",
|
"source-map-explorer": "^2.5.0",
|
||||||
|
7
src/@types/MetaData.d.ts
vendored
7
src/@types/MetaData.d.ts
vendored
@ -4,12 +4,11 @@ import {
|
|||||||
AdditionalInformation,
|
AdditionalInformation,
|
||||||
ServiceMetadata
|
ServiceMetadata
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { PriceOptions, DataTokenOptions } from '@oceanprotocol/react'
|
import { DataTokenOptions, PriceOptions } from '@oceanprotocol/react'
|
||||||
|
|
||||||
export interface AdditionalInformationMarket extends AdditionalInformation {
|
export interface AdditionalInformationMarket extends AdditionalInformation {
|
||||||
links?: File[]
|
links?: File[]
|
||||||
termsAndConditions: boolean
|
termsAndConditions: boolean
|
||||||
priceType: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetadataMarket extends Metadata {
|
export interface MetadataMarket extends Metadata {
|
||||||
@ -22,8 +21,6 @@ export interface MetadataMarket extends Metadata {
|
|||||||
export interface PriceOptionsMarket extends PriceOptions {
|
export interface PriceOptionsMarket extends PriceOptions {
|
||||||
// easier to keep this as number for Yup input validation
|
// easier to keep this as number for Yup input validation
|
||||||
swapFee: number
|
swapFee: number
|
||||||
// collect datatoken info for publishing
|
|
||||||
datatoken?: DataTokenOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetadataPublishForm {
|
export interface MetadataPublishForm {
|
||||||
@ -33,7 +30,7 @@ export interface MetadataPublishForm {
|
|||||||
files: string | File[]
|
files: string | File[]
|
||||||
author: string
|
author: string
|
||||||
license: string
|
license: string
|
||||||
price: PriceOptionsMarket
|
dataTokenOptions: DataTokenOptions
|
||||||
access: 'Download' | 'Compute' | string
|
access: 'Download' | 'Compute' | string
|
||||||
termsAndConditions: boolean
|
termsAndConditions: boolean
|
||||||
// ---- optional fields ----
|
// ---- optional fields ----
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
/* States */
|
/* States */
|
||||||
.error {
|
.error {
|
||||||
border-color: var(--rbrand-alert-ed);
|
border-color: var(--rbrand-alert-ed);
|
||||||
@ -43,7 +47,7 @@
|
|||||||
|
|
||||||
.info {
|
.info {
|
||||||
border-color: var(--brand-alert-yellow);
|
border-color: var(--brand-alert-yellow);
|
||||||
color: var(--brand-alert-yellow);
|
color: #9f7e19;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
|
@ -1,19 +1,35 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, FormEvent } from 'react'
|
||||||
import styles from './Alert.module.css'
|
import styles from './Alert.module.css'
|
||||||
|
import Button from './Button'
|
||||||
|
|
||||||
export default function Alert({
|
export default function Alert({
|
||||||
title,
|
title,
|
||||||
text,
|
text,
|
||||||
state
|
state,
|
||||||
|
action
|
||||||
}: {
|
}: {
|
||||||
title?: string
|
title?: string
|
||||||
text: string
|
text: string
|
||||||
state: 'error' | 'warning' | 'info' | 'success'
|
state: 'error' | 'warning' | 'info' | 'success'
|
||||||
|
action?: {
|
||||||
|
name: string
|
||||||
|
handleAction: (e: FormEvent<HTMLButtonElement>) => void
|
||||||
|
}
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.alert} ${styles[state]}`}>
|
<div className={`${styles.alert} ${styles[state]}`}>
|
||||||
{title && <h3 className={styles.title}>{title}</h3>}
|
{title && <h3 className={styles.title}>{title}</h3>}
|
||||||
<p className={styles.text}>{text}</p>
|
<p className={styles.text}>{text}</p>
|
||||||
|
{action && (
|
||||||
|
<Button
|
||||||
|
className={styles.action}
|
||||||
|
size="small"
|
||||||
|
style="primary"
|
||||||
|
onClick={action.handleAction}
|
||||||
|
>
|
||||||
|
{action.name}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ input[type='range']::-moz-range-track {
|
|||||||
|
|
||||||
.small {
|
.small {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
min-height: 34px;
|
height: 34px;
|
||||||
padding: calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ input[type='range']::-moz-range-track {
|
|||||||
|
|
||||||
.prefix.small,
|
.prefix.small,
|
||||||
.postfix.small {
|
.postfix.small {
|
||||||
min-height: 34px;
|
height: 34px;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ 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 Price from '../../molecules/FormFields/Price'
|
import Datatoken from '../../molecules/FormFields/Datatoken'
|
||||||
|
|
||||||
const DefaultInput = ({
|
const DefaultInput = ({
|
||||||
small,
|
small,
|
||||||
@ -87,8 +87,8 @@ export default function InputElement({
|
|||||||
)
|
)
|
||||||
case 'files':
|
case 'files':
|
||||||
return <FilesInput name={name} {...field} {...props} />
|
return <FilesInput name={name} {...field} {...props} />
|
||||||
case 'price':
|
case 'datatoken':
|
||||||
return <Price 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} />
|
||||||
default:
|
default:
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
.loaderWrap {
|
.loaderWrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
display: block;
|
display: block;
|
||||||
width: 20px;
|
flex: 0 0 1.2rem;
|
||||||
height: 20px;
|
width: 1.2rem;
|
||||||
|
height: 1.2rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 2px solid var(--brand-purple);
|
border: 2px solid var(--brand-purple);
|
||||||
border-top-color: var(--brand-violet);
|
border-top-color: var(--brand-violet);
|
||||||
|
@ -28,7 +28,7 @@ export default function SuccessConfetti({
|
|||||||
action
|
action
|
||||||
}: {
|
}: {
|
||||||
success: string
|
success: string
|
||||||
action: ReactNode
|
action?: ReactNode
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
// Have some confetti upon success
|
// Have some confetti upon success
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
.datatoken {
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
padding: calc(var(--spacer) / 3);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
32
src/components/molecules/FormFields/Datatoken/index.tsx
Normal file
32
src/components/molecules/FormFields/Datatoken/index.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { useField } from 'formik'
|
||||||
|
import { InputProps } from '../../../atoms/Input'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
|
||||||
|
import RefreshName from './RefreshName'
|
||||||
|
|
||||||
|
export default function Datatoken(props: InputProps): ReactElement {
|
||||||
|
const { ocean } = useOcean()
|
||||||
|
const [field, meta, helpers] = useField(props.name)
|
||||||
|
|
||||||
|
function generateName() {
|
||||||
|
if (!ocean) return
|
||||||
|
const dataTokenOptions = ocean.datatokens.generateDtName()
|
||||||
|
helpers.setValue({ ...dataTokenOptions })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate new DT name & symbol
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ocean) return
|
||||||
|
generateName()
|
||||||
|
}, [ocean])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.datatoken}>
|
||||||
|
<strong>{field?.value?.name}</strong> —{' '}
|
||||||
|
<strong>{field?.value?.symbol}</strong>
|
||||||
|
<RefreshName generateName={generateName} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,116 +0,0 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
|
||||||
import { graphql, useStaticQuery } from 'gatsby'
|
|
||||||
import { InputProps } from '../../../atoms/Input'
|
|
||||||
import styles from './index.module.css'
|
|
||||||
import Tabs from '../../../atoms/Tabs'
|
|
||||||
import Fixed from './Fixed'
|
|
||||||
import Dynamic from './Dynamic'
|
|
||||||
import { useField, useFormikContext } from 'formik'
|
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
|
||||||
import { PriceOptionsMarket } from '../../../../@types/MetaData'
|
|
||||||
|
|
||||||
const query = graphql`
|
|
||||||
query PriceFieldQuery {
|
|
||||||
content: allFile(filter: { relativePath: { eq: "pages/publish.json" } }) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
childPagesJson {
|
|
||||||
price {
|
|
||||||
fixed {
|
|
||||||
title
|
|
||||||
info
|
|
||||||
}
|
|
||||||
dynamic {
|
|
||||||
title
|
|
||||||
info
|
|
||||||
tooltips {
|
|
||||||
poolInfo
|
|
||||||
swapFee
|
|
||||||
communityFee
|
|
||||||
marketplaceFee
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default function Price(props: InputProps): ReactElement {
|
|
||||||
const { debug } = useUserPreferences()
|
|
||||||
const data = useStaticQuery(query)
|
|
||||||
const content = data.content.edges[0].node.childPagesJson.price
|
|
||||||
const { ocean } = useOcean()
|
|
||||||
|
|
||||||
const [field, meta, helpers] = useField(props.name)
|
|
||||||
const { price }: PriceOptionsMarket = field.value
|
|
||||||
|
|
||||||
const [tokensToMint, setTokensToMint] = useState<number>()
|
|
||||||
|
|
||||||
function handleTabChange(tabName: string) {
|
|
||||||
const type = tabName.toLowerCase()
|
|
||||||
helpers.setValue({ ...field.value, type })
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateName() {
|
|
||||||
if (!ocean) return
|
|
||||||
const datatoken = ocean.datatokens.generateDtName()
|
|
||||||
helpers.setValue({ ...field.value, datatoken })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always update everything when amountOcean changes
|
|
||||||
useEffect(() => {
|
|
||||||
const tokensToMint = Number(price) * Number(field.value.weightOnDataToken)
|
|
||||||
setTokensToMint(tokensToMint)
|
|
||||||
helpers.setValue({ ...field.value, tokensToMint })
|
|
||||||
}, [price])
|
|
||||||
|
|
||||||
// Generate new DT name & symbol, but only once automatically
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ocean || typeof field?.value?.datatoken?.name !== 'undefined') return
|
|
||||||
generateName()
|
|
||||||
}, [ocean])
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
title: content.fixed.title,
|
|
||||||
content: (
|
|
||||||
<Fixed
|
|
||||||
datatokenOptions={field.value.datatoken}
|
|
||||||
generateName={generateName}
|
|
||||||
content={content.fixed}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: content.dynamic.title,
|
|
||||||
content: (
|
|
||||||
<Dynamic
|
|
||||||
ocean={price}
|
|
||||||
priceOptions={{ ...field.value, tokensToMint }}
|
|
||||||
datatokenOptions={field.value.datatoken}
|
|
||||||
generateName={generateName}
|
|
||||||
content={content.dynamic}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.price}>
|
|
||||||
<Tabs
|
|
||||||
items={tabs}
|
|
||||||
handleTabChange={handleTabChange}
|
|
||||||
defaultIndex={field?.value?.type === 'fixed' ? 0 : 1}
|
|
||||||
/>
|
|
||||||
{debug === true && (
|
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify(field.value, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -22,3 +22,7 @@
|
|||||||
.feedback {
|
.feedback {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hasTokens {
|
||||||
|
composes: hasTokens from './index.module.css';
|
||||||
|
}
|
||||||
|
@ -13,5 +13,5 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Default = (): ReactElement => (
|
export const Default = (): ReactElement => (
|
||||||
<Compute ddo={ddo as DDO} isBalanceSufficient />
|
<Compute ddo={ddo as DDO} dtBalance="1" isBalanceSufficient />
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,8 @@ import {
|
|||||||
computeOptions,
|
computeOptions,
|
||||||
useCompute,
|
useCompute,
|
||||||
readFileContent,
|
readFileContent,
|
||||||
useOcean
|
useOcean,
|
||||||
|
usePricing
|
||||||
} from '@oceanprotocol/react'
|
} from '@oceanprotocol/react'
|
||||||
import styles from './Compute.module.css'
|
import styles from './Compute.module.css'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
@ -19,14 +20,19 @@ import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
|||||||
|
|
||||||
export default function Compute({
|
export default function Compute({
|
||||||
ddo,
|
ddo,
|
||||||
isBalanceSufficient
|
isBalanceSufficient,
|
||||||
|
dtBalance
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
isBalanceSufficient: boolean
|
isBalanceSufficient: boolean
|
||||||
|
dtBalance: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const { marketFeeAddress } = useSiteMetadata()
|
||||||
|
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { compute, isLoading, computeStepText, computeError } = useCompute()
|
const { compute, isLoading, computeStepText, computeError } = useCompute()
|
||||||
const { marketFeeAddress } = useSiteMetadata()
|
const { buyDT, dtSymbol } = usePricing(ddo)
|
||||||
|
|
||||||
const computeService = ddo.findServiceByType('compute')
|
const computeService = ddo.findServiceByType('compute')
|
||||||
const metadataService = ddo.findServiceByType('metadata')
|
const metadataService = ddo.findServiceByType('metadata')
|
||||||
|
|
||||||
@ -46,6 +52,7 @@ export default function Compute({
|
|||||||
computeType === '' ||
|
computeType === '' ||
|
||||||
!ocean ||
|
!ocean ||
|
||||||
!isBalanceSufficient
|
!isBalanceSufficient
|
||||||
|
const hasDatatoken = Number(dtBalance) >= 1
|
||||||
|
|
||||||
const onDrop = async (files: File[]) => {
|
const onDrop = async (files: File[]) => {
|
||||||
setFile(files[0])
|
setFile(files[0])
|
||||||
@ -70,6 +77,8 @@ export default function Compute({
|
|||||||
setIsPublished(false)
|
setIsPublished(false)
|
||||||
setError('')
|
setError('')
|
||||||
|
|
||||||
|
!hasDatatoken && (await buyDT('1'))
|
||||||
|
|
||||||
await compute(
|
await compute(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService,
|
computeService,
|
||||||
@ -97,6 +106,12 @@ export default function Compute({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.pricewrapper}>
|
<div className={styles.pricewrapper}>
|
||||||
<Price ddo={ddo} conversion />
|
<Price ddo={ddo} conversion />
|
||||||
|
{hasDatatoken && (
|
||||||
|
<div className={styles.hasTokens}>
|
||||||
|
You own {dtBalance} {dtSymbol} allowing you to use this data set
|
||||||
|
without paying again.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -118,7 +133,7 @@ export default function Compute({
|
|||||||
onClick={() => startJob()}
|
onClick={() => startJob()}
|
||||||
disabled={isComputeButtonDisabled}
|
disabled={isComputeButtonDisabled}
|
||||||
>
|
>
|
||||||
Start job
|
{hasDatatoken ? 'Start job' : 'Buy'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -12,10 +12,11 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pricewrapper button {
|
.actions {
|
||||||
margin-top: var(--spacer);
|
width: 100%;
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback {
|
.hasTokens {
|
||||||
width: 100%;
|
composes: hasTokens from './index.module.css';
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ export default {
|
|||||||
export const PricedAsset = (): ReactElement => (
|
export const PricedAsset = (): ReactElement => (
|
||||||
<Consume
|
<Consume
|
||||||
ddo={ddo as DDO}
|
ddo={ddo as DDO}
|
||||||
|
dtBalance="1"
|
||||||
isBalanceSufficient
|
isBalanceSufficient
|
||||||
file={new DDO(ddo).findServiceByType('metadata').attributes.main.files[0]}
|
file={new DDO(ddo).findServiceByType('metadata').attributes.main.files[0]}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
|
import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
@ -7,41 +7,56 @@ import Price from '../../atoms/Price'
|
|||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
||||||
import styles from './Consume.module.css'
|
import styles from './Consume.module.css'
|
||||||
import Loader from '../../atoms/Loader'
|
import Loader from '../../atoms/Loader'
|
||||||
import { useOcean, useConsume } from '@oceanprotocol/react'
|
import { useOcean, useConsume, usePricing } from '@oceanprotocol/react'
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
|
|
||||||
export default function Consume({
|
export default function Consume({
|
||||||
ddo,
|
ddo,
|
||||||
file,
|
file,
|
||||||
isBalanceSufficient
|
isBalanceSufficient,
|
||||||
|
dtBalance
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
file: FileMetadata
|
file: FileMetadata
|
||||||
isBalanceSufficient: boolean
|
isBalanceSufficient: boolean
|
||||||
|
dtBalance: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
const { marketFeeAddress } = useSiteMetadata()
|
const { marketFeeAddress } = useSiteMetadata()
|
||||||
|
const {
|
||||||
|
dtSymbol,
|
||||||
|
buyDT,
|
||||||
|
pricingStepText,
|
||||||
|
pricingError,
|
||||||
|
pricingIsLoading
|
||||||
|
} = usePricing(ddo)
|
||||||
const { consumeStepText, consume, consumeError } = useConsume()
|
const { consumeStepText, consume, consumeError } = useConsume()
|
||||||
|
|
||||||
const isDisabled = !ocean || !isBalanceSufficient
|
const isDisabled =
|
||||||
|
!ocean ||
|
||||||
|
!isBalanceSufficient ||
|
||||||
|
typeof consumeStepText !== 'undefined' ||
|
||||||
|
pricingIsLoading
|
||||||
|
const hasDatatoken = Number(dtBalance) >= 1
|
||||||
|
|
||||||
if (consumeError) {
|
async function handleConsume() {
|
||||||
toast.error(consumeError)
|
!hasDatatoken && (await buyDT('1'))
|
||||||
|
await consume(ddo.id, ddo.dataToken, 'access', marketFeeAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Output errors in UI
|
||||||
|
useEffect(() => {
|
||||||
|
consumeError && toast.error(consumeError)
|
||||||
|
pricingError && toast.error(pricingError)
|
||||||
|
}, [consumeError, pricingError])
|
||||||
|
|
||||||
const PurchaseButton = () => (
|
const PurchaseButton = () => (
|
||||||
<div>
|
<div className={styles.actions}>
|
||||||
{consumeStepText ? (
|
{consumeStepText || pricingIsLoading ? (
|
||||||
<Loader message={consumeStepText} />
|
<Loader message={consumeStepText || pricingStepText} />
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button style="primary" onClick={handleConsume} disabled={isDisabled}>
|
||||||
style="primary"
|
{hasDatatoken ? 'Download' : 'Buy'}
|
||||||
onClick={() =>
|
|
||||||
consume(ddo.id, ddo.dataToken, 'access', marketFeeAddress)
|
|
||||||
}
|
|
||||||
disabled={isDisabled}
|
|
||||||
>
|
|
||||||
Buy
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -55,6 +70,12 @@ export default function Consume({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.pricewrapper}>
|
<div className={styles.pricewrapper}>
|
||||||
<Price ddo={ddo} conversion />
|
<Price ddo={ddo} conversion />
|
||||||
|
{hasDatatoken && (
|
||||||
|
<div className={styles.hasTokens}>
|
||||||
|
You own {dtBalance} {dtSymbol} allowing you to use this data set
|
||||||
|
without paying again.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<PurchaseButton />
|
<PurchaseButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { useOcean, useMetadata } from '@oceanprotocol/react'
|
import { useOcean, useMetadata, usePricing } from '@oceanprotocol/react'
|
||||||
import { DDO, Logger } from '@oceanprotocol/lib'
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import stylesActions from './Actions.module.css'
|
import stylesActions from './Actions.module.css'
|
||||||
@ -45,10 +45,10 @@ export default function Pool({ ddo }: { ddo: DDO }): ReactElement {
|
|||||||
|
|
||||||
const { ocean, accountId } = useOcean()
|
const { ocean, accountId } = useOcean()
|
||||||
const { price } = useMetadata(ddo)
|
const { price } = useMetadata(ddo)
|
||||||
|
const { dtSymbol } = usePricing(ddo)
|
||||||
|
|
||||||
const [poolTokens, setPoolTokens] = useState<string>()
|
const [poolTokens, setPoolTokens] = useState<string>()
|
||||||
const [totalPoolTokens, setTotalPoolTokens] = useState<string>()
|
const [totalPoolTokens, setTotalPoolTokens] = useState<string>()
|
||||||
const [dtSymbol, setDtSymbol] = useState<string>()
|
|
||||||
const [userLiquidity, setUserLiquidity] = useState<Balance>()
|
const [userLiquidity, setUserLiquidity] = useState<Balance>()
|
||||||
const [swapFee, setSwapFee] = useState<string>()
|
const [swapFee, setSwapFee] = useState<string>()
|
||||||
|
|
||||||
@ -79,12 +79,6 @@ export default function Pool({ ddo }: { ddo: DDO }): ReactElement {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//
|
|
||||||
// Get data token symbol
|
|
||||||
//
|
|
||||||
const dtSymbol = await ocean.datatokens.getSymbol(ddo.dataToken)
|
|
||||||
setDtSymbol(dtSymbol)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get everything which is in the pool
|
// Get everything which is in the pool
|
||||||
//
|
//
|
||||||
|
@ -4,3 +4,9 @@
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hasTokens {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-top: calc(var(--spacer) / 12);
|
||||||
|
}
|
||||||
|
@ -2,37 +2,63 @@ import React, { ReactElement, useState, useEffect } from 'react'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import Compute from './Compute'
|
import Compute from './Compute'
|
||||||
import Consume from './Consume'
|
import Consume from './Consume'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
import Tabs from '../../atoms/Tabs'
|
import Tabs from '../../atoms/Tabs'
|
||||||
import { useOcean, useMetadata } from '@oceanprotocol/react'
|
import { useOcean, useMetadata } from '@oceanprotocol/react'
|
||||||
import compareAsBN from '../../../utils/compareAsBN'
|
import compareAsBN from '../../../utils/compareAsBN'
|
||||||
import Pool from './Pool'
|
import Pool from './Pool'
|
||||||
import { AdditionalInformationMarket } from '../../../@types/MetaData'
|
|
||||||
|
|
||||||
export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement {
|
export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement {
|
||||||
const { balance } = useOcean()
|
const { ocean, balance, accountId } = useOcean()
|
||||||
const { price } = useMetadata(ddo)
|
const { price } = useMetadata(ddo)
|
||||||
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
|
||||||
|
const [dtBalance, setDtBalance] = useState<string>()
|
||||||
|
|
||||||
const isCompute = Boolean(ddo.findServiceByType('compute'))
|
const isCompute = Boolean(ddo.findServiceByType('compute'))
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
|
|
||||||
|
// Get and set user DT balance
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ocean || !accountId) return
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
try {
|
||||||
|
const dtBalance = await ocean.datatokens.balance(
|
||||||
|
ddo.dataToken,
|
||||||
|
accountId
|
||||||
|
)
|
||||||
|
setDtBalance(dtBalance)
|
||||||
|
} catch (e) {
|
||||||
|
Logger.error(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [ocean, accountId, ddo.dataToken])
|
||||||
|
|
||||||
// Check user balance against price
|
// Check user balance against price
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!price || !price.value || !balance || !balance.ocean) return
|
if (!price || !price.value || !balance || !balance.ocean || !dtBalance)
|
||||||
|
return
|
||||||
|
|
||||||
setIsBalanceSufficient(compareAsBN(balance.ocean, `${price.value}`))
|
setIsBalanceSufficient(
|
||||||
|
compareAsBN(balance.ocean, `${price.value}`) || Number(dtBalance) >= 1
|
||||||
|
)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setIsBalanceSufficient(false)
|
setIsBalanceSufficient(false)
|
||||||
}
|
}
|
||||||
}, [balance, price])
|
}, [balance, price, dtBalance])
|
||||||
|
|
||||||
const UseContent = isCompute ? (
|
const UseContent = isCompute ? (
|
||||||
<Compute ddo={ddo} isBalanceSufficient={isBalanceSufficient} />
|
<Compute
|
||||||
|
ddo={ddo}
|
||||||
|
dtBalance={dtBalance}
|
||||||
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Consume
|
<Consume
|
||||||
ddo={ddo}
|
ddo={ddo}
|
||||||
|
dtBalance={dtBalance}
|
||||||
isBalanceSufficient={isBalanceSufficient}
|
isBalanceSufficient={isBalanceSufficient}
|
||||||
file={attributes.main.files[0]}
|
file={attributes.main.files[0]}
|
||||||
/>
|
/>
|
||||||
@ -46,10 +72,7 @@ export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Check from metadata, cause that is available earlier
|
// Check from metadata, cause that is available earlier
|
||||||
const hasPool =
|
const hasPool = ddo.price?.type === 'pool'
|
||||||
((attributes.additionalInformation as unknown) as AdditionalInformationMarket)
|
|
||||||
?.priceType === 'dynamic'
|
|
||||||
// price?.type === 'pool'
|
|
||||||
|
|
||||||
hasPool &&
|
hasPool &&
|
||||||
tabs.push({
|
tabs.push({
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Time from '../../atoms/Time'
|
import Time from '../../atoms/Time'
|
||||||
import MetaItem from './MetaItem'
|
import MetaItem from './MetaItem'
|
||||||
import styles from './MetaFull.module.css'
|
import styles from './MetaFull.module.css'
|
||||||
import { MetadataMarket } from '../../../@types/MetaData'
|
import { MetadataMarket } from '../../../@types/MetaData'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import EtherscanLink from '../../atoms/EtherscanLink'
|
import EtherscanLink from '../../atoms/EtherscanLink'
|
||||||
|
import { usePricing } from '@oceanprotocol/react'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
|
||||||
|
|
||||||
export default function MetaFull({
|
export default function MetaFull({
|
||||||
ddo,
|
ddo,
|
||||||
@ -15,24 +14,9 @@ export default function MetaFull({
|
|||||||
ddo: DDO
|
ddo: DDO
|
||||||
metadata: MetadataMarket
|
metadata: MetadataMarket
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean } = useOcean()
|
|
||||||
const { id, dataToken } = ddo
|
const { id, dataToken } = ddo
|
||||||
const { dateCreated, datePublished, author, license } = metadata.main
|
const { dateCreated, datePublished, author, license } = metadata.main
|
||||||
|
const { dtSymbol, dtName } = usePricing(ddo)
|
||||||
const [dtName, setDtName] = useState<string>()
|
|
||||||
const [dtSymbol, setDtSymbol] = useState<string>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ocean) return
|
|
||||||
|
|
||||||
async function getDataTokenInfo() {
|
|
||||||
const name = await ocean.datatokens.getName(dataToken)
|
|
||||||
setDtName(name)
|
|
||||||
const symbol = await ocean.datatokens.getSymbol(dataToken)
|
|
||||||
setDtSymbol(symbol)
|
|
||||||
}
|
|
||||||
getDataTokenInfo()
|
|
||||||
}, [ocean, dataToken])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.metaFull}>
|
<div className={styles.metaFull}>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
.feedback {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 20vh;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
34
src/components/organisms/AssetContent/Pricing/Feedback.tsx
Normal file
34
src/components/organisms/AssetContent/Pricing/Feedback.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Loader from '../../../atoms/Loader'
|
||||||
|
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './Feedback.module.css'
|
||||||
|
import Button from '../../../atoms/Button'
|
||||||
|
|
||||||
|
export default function Feedback({
|
||||||
|
success,
|
||||||
|
pricingStepText
|
||||||
|
}: {
|
||||||
|
success: string
|
||||||
|
pricingStepText: string
|
||||||
|
}): ReactElement {
|
||||||
|
const SuccessAction = () => (
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
size="small"
|
||||||
|
className={styles.action}
|
||||||
|
onClick={() => window?.location.reload()}
|
||||||
|
>
|
||||||
|
Reload Page
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.feedback}>
|
||||||
|
{success ? (
|
||||||
|
<SuccessConfetti success={success} action={<SuccessAction />} />
|
||||||
|
) : (
|
||||||
|
<Loader message={pricingStepText} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
composes: box from '../../../atoms/Box.module.css';
|
composes: box from '../../../../atoms/Box.module.css';
|
||||||
padding: calc(var(--spacer) / 1.5);
|
padding: calc(var(--spacer) / 1.5);
|
||||||
width: 6rem;
|
width: 6rem;
|
||||||
height: 6rem;
|
height: 6rem;
|
@ -1,11 +1,10 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
import styles from './Coin.module.css'
|
import styles from './Coin.module.css'
|
||||||
import InputElement from '../../../atoms/Input/InputElement'
|
import InputElement from '../../../../atoms/Input/InputElement'
|
||||||
import { ReactComponent as Logo } from '../../../../images/logo.svg'
|
import { ReactComponent as Logo } from '../../../../../images/logo.svg'
|
||||||
import Conversion from '../../../atoms/Price/Conversion'
|
import Conversion from '../../../../atoms/Price/Conversion'
|
||||||
import { DataTokenOptions } from '@oceanprotocol/react'
|
import { DataTokenOptions } from '@oceanprotocol/react'
|
||||||
import RefreshName from './RefreshName'
|
|
||||||
import { useField } from 'formik'
|
import { useField } from 'formik'
|
||||||
import Error from './Error'
|
import Error from './Error'
|
||||||
|
|
||||||
@ -13,13 +12,11 @@ export default function Coin({
|
|||||||
datatokenOptions,
|
datatokenOptions,
|
||||||
name,
|
name,
|
||||||
weight,
|
weight,
|
||||||
generateName,
|
|
||||||
readOnly
|
readOnly
|
||||||
}: {
|
}: {
|
||||||
datatokenOptions: DataTokenOptions
|
datatokenOptions: DataTokenOptions
|
||||||
name: string
|
name: string
|
||||||
weight: string
|
weight: string
|
||||||
generateName?: () => void
|
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [field, meta] = useField(name)
|
const [field, meta] = useField(name)
|
||||||
@ -32,9 +29,6 @@ export default function Coin({
|
|||||||
|
|
||||||
<h4 className={styles.tokenName}>
|
<h4 className={styles.tokenName}>
|
||||||
{datatokenOptions?.name || 'Data Token'}
|
{datatokenOptions?.name || 'Data Token'}
|
||||||
{datatokenOptions?.name && typeof generateName === 'function' && (
|
|
||||||
<RefreshName generateName={generateName} />
|
|
||||||
)}
|
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className={styles.weight}>
|
<div className={styles.weight}>
|
||||||
@ -47,6 +41,8 @@ export default function Coin({
|
|||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
prefix={datatokenOptions?.symbol || 'DT'}
|
prefix={datatokenOptions?.symbol || 'DT'}
|
||||||
min="1"
|
min="1"
|
||||||
|
name={name}
|
||||||
|
value={field.value}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
{datatokenOptions?.symbol === 'OCEAN' && (
|
{datatokenOptions?.symbol === 'OCEAN' && (
|
@ -40,6 +40,7 @@
|
|||||||
margin-left: -2rem;
|
margin-left: -2rem;
|
||||||
margin-right: -2rem;
|
margin-right: -2rem;
|
||||||
border-bottom: 1px solid var(--brand-grey-lighter);
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
background: var(--brand-grey-dimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 40rem) {
|
@media screen and (min-width: 40rem) {
|
@ -1,34 +1,34 @@
|
|||||||
import { DataTokenOptions, useOcean } from '@oceanprotocol/react'
|
import { useOcean, usePricing } from '@oceanprotocol/react'
|
||||||
import PriceUnit from '../../../atoms/Price/PriceUnit'
|
import PriceUnit from '../../../../atoms/Price/PriceUnit'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { PriceOptionsMarket } from '../../../../@types/MetaData'
|
import { useSiteMetadata } from '../../../../../hooks/useSiteMetadata'
|
||||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
import { isCorrectNetwork } from '../../../../../utils/wallet'
|
||||||
import { isCorrectNetwork } from '../../../../utils/wallet'
|
import Alert from '../../../../atoms/Alert'
|
||||||
import Alert from '../../../atoms/Alert'
|
import FormHelp from '../../../../atoms/Input/Help'
|
||||||
import FormHelp from '../../../atoms/Input/Help'
|
import Tooltip from '../../../../atoms/Tooltip'
|
||||||
import Tooltip from '../../../atoms/Tooltip'
|
import Wallet from '../../../../molecules/Wallet'
|
||||||
import Wallet from '../../Wallet'
|
|
||||||
import Coin from './Coin'
|
import Coin from './Coin'
|
||||||
import styles from './Dynamic.module.css'
|
import styles from './Dynamic.module.css'
|
||||||
import Fees from './Fees'
|
import Fees from './Fees'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import { PriceOptionsMarket } from '../../../../../@types/MetaData'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export default function Dynamic({
|
export default function Dynamic({
|
||||||
ocean,
|
ddo,
|
||||||
priceOptions,
|
|
||||||
datatokenOptions,
|
|
||||||
generateName,
|
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
ocean: number
|
ddo: DDO
|
||||||
priceOptions: PriceOptionsMarket
|
|
||||||
datatokenOptions: DataTokenOptions
|
|
||||||
generateName: () => void
|
|
||||||
content: any
|
content: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
const { account, balance, networkId, refreshBalance } = useOcean()
|
const { account, balance, networkId, refreshBalance } = useOcean()
|
||||||
const { weightOnDataToken } = priceOptions
|
const { dtSymbol, dtName } = usePricing(ddo)
|
||||||
|
|
||||||
|
// Connect with form
|
||||||
|
const { values } = useFormikContext()
|
||||||
|
const { price, weightOnDataToken } = values as PriceOptionsMarket
|
||||||
|
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const correctNetwork = isCorrectNetwork(networkId)
|
const correctNetwork = isCorrectNetwork(networkId)
|
||||||
@ -42,12 +42,12 @@ export default function Dynamic({
|
|||||||
setError(`No account connected. Please connect your Web3 wallet.`)
|
setError(`No account connected. Please connect your Web3 wallet.`)
|
||||||
} else if (!correctNetwork) {
|
} else if (!correctNetwork) {
|
||||||
setError(`Wrong Network. Please connect to ${desiredNetworkName}.`)
|
setError(`Wrong Network. Please connect to ${desiredNetworkName}.`)
|
||||||
} else if (Number(balance.ocean) < Number(ocean)) {
|
} else if (Number(balance.ocean) < Number(price)) {
|
||||||
setError(`Insufficient balance. You need at least ${ocean} OCEAN`)
|
setError(`Insufficient balance. You need at least ${price} OCEAN`)
|
||||||
} else {
|
} else {
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
}
|
}
|
||||||
}, [ocean, networkId, account, balance, correctNetwork, desiredNetworkName])
|
}, [price, networkId, account, balance, correctNetwork, desiredNetworkName])
|
||||||
|
|
||||||
// refetch balance periodically
|
// refetch balance periodically
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -59,7 +59,7 @@ export default function Dynamic({
|
|||||||
return () => {
|
return () => {
|
||||||
clearInterval(balanceInterval)
|
clearInterval(balanceInterval)
|
||||||
}
|
}
|
||||||
}, [ocean, networkId, account])
|
}, [networkId, account])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.dynamic}>
|
<div className={styles.dynamic}>
|
||||||
@ -83,15 +83,14 @@ export default function Dynamic({
|
|||||||
|
|
||||||
<div className={styles.tokens}>
|
<div className={styles.tokens}>
|
||||||
<Coin
|
<Coin
|
||||||
name="price.price"
|
name="price"
|
||||||
datatokenOptions={{ symbol: 'OCEAN', name: 'Ocean Token' }}
|
datatokenOptions={{ symbol: 'OCEAN', name: 'Ocean Token' }}
|
||||||
weight={`${100 - Number(Number(weightOnDataToken) * 10)}%`}
|
weight={`${100 - Number(Number(weightOnDataToken) * 10)}%`}
|
||||||
/>
|
/>
|
||||||
<Coin
|
<Coin
|
||||||
name="price.tokensToMint"
|
name="dtAmount"
|
||||||
datatokenOptions={datatokenOptions}
|
datatokenOptions={{ symbol: dtSymbol, name: dtName }}
|
||||||
weight={`${Number(weightOnDataToken) * 10}%`}
|
weight={`${Number(weightOnDataToken) * 10}%`}
|
||||||
generateName={generateName}
|
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
@ -1,6 +1,6 @@
|
|||||||
import { FieldMetaProps } from 'formik'
|
import { FieldMetaProps } from 'formik'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import stylesInput from '../../../atoms/Input/index.module.css'
|
import stylesInput from '../../../../atoms/Input/index.module.css'
|
||||||
|
|
||||||
export default function Error({
|
export default function Error({
|
||||||
meta
|
meta
|
@ -1,16 +1,16 @@
|
|||||||
.fees {
|
.fees {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
||||||
margin-left: -2rem;
|
margin-left: -2rem;
|
||||||
margin-right: -2rem;
|
margin-right: -2rem;
|
||||||
border-bottom: 1px solid var(--brand-grey-lighter);
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
margin-top: var(--spacer);
|
padding: var(--spacer) var(--spacer) calc(var(--spacer) / 2) var(--spacer);
|
||||||
padding: 0 var(--spacer) calc(var(--spacer) / 2) var(--spacer);
|
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom: 1px solid var(--brand-grey-lighter);
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
background: var(--brand-grey-dimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fees label {
|
.fees label {
|
@ -1,18 +1,40 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Tooltip from '../../../atoms/Tooltip'
|
import Tooltip from '../../../../atoms/Tooltip'
|
||||||
import styles from './Fees.module.css'
|
import styles from './Fees.module.css'
|
||||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
import { useField, useFormikContext } from 'formik'
|
||||||
import { useField } from 'formik'
|
import Input from '../../../../atoms/Input'
|
||||||
import Input from '../../../atoms/Input'
|
|
||||||
import Error from './Error'
|
import Error from './Error'
|
||||||
|
|
||||||
|
const Default = ({
|
||||||
|
title,
|
||||||
|
name,
|
||||||
|
tooltip
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
name: string
|
||||||
|
tooltip: string
|
||||||
|
}) => (
|
||||||
|
<Input
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
{title}
|
||||||
|
<Tooltip content={tooltip} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
value="0.1"
|
||||||
|
name={name}
|
||||||
|
postfix="%"
|
||||||
|
readOnly
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
export default function Fees({
|
export default function Fees({
|
||||||
tooltips
|
tooltips
|
||||||
}: {
|
}: {
|
||||||
tooltips: { [key: string]: string }
|
tooltips: { [key: string]: string }
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useSiteMetadata()
|
const [field, meta] = useField('swapFee')
|
||||||
const [field, meta] = useField('price.swapFee')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -34,32 +56,16 @@ export default function Fees({
|
|||||||
additionalComponent={<Error meta={meta} />}
|
additionalComponent={<Error meta={meta} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Default
|
||||||
label={
|
title="Community Fee"
|
||||||
<>
|
|
||||||
Community Fee
|
|
||||||
<Tooltip content={tooltips.communityFee} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
value="0.1"
|
|
||||||
name="communityFee"
|
name="communityFee"
|
||||||
postfix="%"
|
tooltip={tooltips.communityFee}
|
||||||
readOnly
|
|
||||||
small
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Default
|
||||||
label={
|
title="Marketplace Fee"
|
||||||
<>
|
|
||||||
Marketplace Fee
|
|
||||||
<Tooltip content={tooltips.marketplaceFee} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
value="0.1"
|
|
||||||
name="marketplaceFee"
|
name="marketplaceFee"
|
||||||
postfix="%"
|
tooltip={tooltips.marketplaceFee}
|
||||||
readOnly
|
|
||||||
small
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
@ -14,11 +14,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
margin-top: var(--spacer);
|
margin-left: -2rem;
|
||||||
|
margin-right: -2rem;
|
||||||
|
padding-top: var(--spacer);
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
background: var(--brand-grey-dimmed);
|
||||||
|
border-top: 1px solid var(--brand-grey-lighter);
|
||||||
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed label {
|
.fixed label {
|
||||||
@ -26,14 +31,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.datatoken {
|
.datatoken {
|
||||||
margin-top: calc(var(--spacer) / 6);
|
margin-top: calc(var(--spacer) / 2);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.datatoken h4 {
|
.datatoken h4 {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-base);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
@ -1,24 +1,23 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
import styles from './Fixed.module.css'
|
import styles from './Fixed.module.css'
|
||||||
import FormHelp from '../../../atoms/Input/Help'
|
import FormHelp from '../../../../atoms/Input/Help'
|
||||||
import Conversion from '../../../atoms/Price/Conversion'
|
import Conversion from '../../../../atoms/Price/Conversion'
|
||||||
import { DataTokenOptions } from '@oceanprotocol/react'
|
|
||||||
import RefreshName from './RefreshName'
|
|
||||||
import { useField } from 'formik'
|
import { useField } from 'formik'
|
||||||
import Input from '../../../atoms/Input'
|
import Input from '../../../../atoms/Input'
|
||||||
import Error from './Error'
|
import Error from './Error'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import { usePricing } from '@oceanprotocol/react'
|
||||||
|
|
||||||
export default function Fixed({
|
export default function Fixed({
|
||||||
datatokenOptions,
|
ddo,
|
||||||
generateName,
|
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
datatokenOptions: DataTokenOptions
|
ddo: DDO
|
||||||
generateName: () => void
|
|
||||||
content: any
|
content: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [field, meta] = useField('price.price')
|
const [field, meta] = useField('price')
|
||||||
|
const { dtName, dtSymbol } = usePricing(ddo)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fixed}>
|
<div className={styles.fixed}>
|
||||||
@ -29,7 +28,7 @@ export default function Fixed({
|
|||||||
<Input
|
<Input
|
||||||
label="Ocean Token"
|
label="Ocean Token"
|
||||||
value={field.value}
|
value={field.value}
|
||||||
name="price.price"
|
name="price"
|
||||||
type="number"
|
type="number"
|
||||||
prefix="OCEAN"
|
prefix="OCEAN"
|
||||||
min="1"
|
min="1"
|
||||||
@ -43,16 +42,11 @@ export default function Fixed({
|
|||||||
/>
|
/>
|
||||||
<Error meta={meta} />
|
<Error meta={meta} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{datatokenOptions && (
|
|
||||||
<div className={styles.datatoken}>
|
<div className={styles.datatoken}>
|
||||||
<h4>
|
<h4>
|
||||||
Data Token <RefreshName generateName={generateName} />
|
= <strong>1</strong> {dtName} — {dtSymbol}
|
||||||
</h4>
|
</h4>
|
||||||
<strong>{datatokenOptions?.name}</strong> —{' '}
|
|
||||||
<strong>{datatokenOptions?.symbol}</strong>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
@ -1,9 +1,3 @@
|
|||||||
.price {
|
|
||||||
border: 1px solid var(--brand-grey-lighter);
|
|
||||||
background: var(--brand-grey-dimmed);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@ -36,3 +30,18 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: calc(var(--spacer) / 1.5);
|
margin-bottom: calc(var(--spacer) / 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button {
|
||||||
|
margin-left: calc(var(--spacer) / 2);
|
||||||
|
margin-right: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionsHelp {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
padding-left: var(--spacer);
|
||||||
|
padding-right: var(--spacer);
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import Tabs from '../../../../atoms/Tabs'
|
||||||
|
import Fixed from './Fixed'
|
||||||
|
import Dynamic from './Dynamic'
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import { useUserPreferences } from '../../../../../providers/UserPreferences'
|
||||||
|
import { PriceOptionsMarket } from '../../../../../@types/MetaData'
|
||||||
|
import Button from '../../../../atoms/Button'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import FormHelp from '../../../../atoms/Input/Help'
|
||||||
|
|
||||||
|
export default function FormPricing({
|
||||||
|
ddo,
|
||||||
|
setShowPricing,
|
||||||
|
content
|
||||||
|
}: {
|
||||||
|
ddo: DDO
|
||||||
|
setShowPricing: (value: boolean) => void
|
||||||
|
content: any
|
||||||
|
}): ReactElement {
|
||||||
|
const { debug } = useUserPreferences()
|
||||||
|
|
||||||
|
// Connect with form
|
||||||
|
const { values, setFieldValue, submitForm } = useFormikContext()
|
||||||
|
const { price, weightOnDataToken, type } = values as PriceOptionsMarket
|
||||||
|
|
||||||
|
// Switch type value upon tab change
|
||||||
|
function handleTabChange(tabName: string) {
|
||||||
|
const type = tabName.toLowerCase()
|
||||||
|
setFieldValue('type', type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always update everything when price value changes
|
||||||
|
useEffect(() => {
|
||||||
|
const dtAmount = Number(price) * Number(weightOnDataToken)
|
||||||
|
setFieldValue('dtAmount', dtAmount)
|
||||||
|
}, [price, weightOnDataToken])
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: content.fixed.title,
|
||||||
|
content: <Fixed content={content.fixed} ddo={ddo} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: content.dynamic.title,
|
||||||
|
content: <Dynamic content={content.dynamic} ddo={ddo} />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tabs
|
||||||
|
items={tabs}
|
||||||
|
handleTabChange={handleTabChange}
|
||||||
|
defaultIndex={type === 'fixed' ? 0 : 1}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.actions}>
|
||||||
|
<Button style="primary" onClick={() => submitForm()}>
|
||||||
|
{content.empty.action.name}
|
||||||
|
</Button>
|
||||||
|
<Button style="text" size="small" onClick={() => setShowPricing(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<FormHelp className={styles.actionsHelp}>
|
||||||
|
{content.empty.action.help}
|
||||||
|
</FormHelp>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{debug === true && (
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(values, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
.pricing {
|
||||||
|
composes: box from '../../../atoms/Box.module.css';
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: var(--spacer);
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing [class*='alert'] {
|
||||||
|
margin: var(--spacer);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
138
src/components/organisms/AssetContent/Pricing/index.tsx
Normal file
138
src/components/organisms/AssetContent/Pricing/index.tsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import React, { FormEvent, ReactElement, useState } from 'react'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { initialValues, validationSchema } from '../../../../models/FormPricing'
|
||||||
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
|
import { usePricing } from '@oceanprotocol/react'
|
||||||
|
import { PriceOptionsMarket } from '../../../../@types/MetaData'
|
||||||
|
import Alert from '../../../atoms/Alert'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import FormPricing from './FormPricing'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import Feedback from './Feedback'
|
||||||
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
|
||||||
|
const query = graphql`
|
||||||
|
query PricingQuery {
|
||||||
|
content: allFile(filter: { relativePath: { eq: "price.json" } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childContentJson {
|
||||||
|
create {
|
||||||
|
empty {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
action {
|
||||||
|
name
|
||||||
|
help
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixed {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
}
|
||||||
|
dynamic {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
tooltips {
|
||||||
|
poolInfo
|
||||||
|
swapFee
|
||||||
|
communityFee
|
||||||
|
marketplaceFee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Pricing({ ddo }: { ddo: DDO }): ReactElement {
|
||||||
|
// Get content
|
||||||
|
const data = useStaticQuery(query)
|
||||||
|
const content = data.content.edges[0].node.childContentJson.create
|
||||||
|
|
||||||
|
// View states
|
||||||
|
const [showPricing, setShowPricing] = useState(false)
|
||||||
|
const [success, setSuccess] = useState<string>()
|
||||||
|
|
||||||
|
const {
|
||||||
|
createPricing,
|
||||||
|
pricingIsLoading,
|
||||||
|
pricingError,
|
||||||
|
pricingStepText
|
||||||
|
} = usePricing(ddo)
|
||||||
|
|
||||||
|
const hasFeedback = pricingIsLoading || typeof success !== 'undefined'
|
||||||
|
|
||||||
|
async function handleCreatePricing(values: PriceOptionsMarket) {
|
||||||
|
try {
|
||||||
|
const priceOptions = {
|
||||||
|
...values,
|
||||||
|
// swapFee is tricky: to get 0.1% you need to send 0.001 as value
|
||||||
|
swapFee: `${values.swapFee / 100}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const tx = await createPricing(priceOptions)
|
||||||
|
|
||||||
|
// Pricing failed
|
||||||
|
if (!tx || pricingError) {
|
||||||
|
toast.error(pricingError || 'Price creation failed.')
|
||||||
|
Logger.error(pricingError || 'Price creation failed.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pricing succeeded
|
||||||
|
setSuccess(
|
||||||
|
`🎉 Successfully created a ${values.type} price. 🎉 Reload the page to get all updates.`
|
||||||
|
)
|
||||||
|
Logger.log(`Transaction: ${tx}`)
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.message)
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleShowPricingForm(e: FormEvent<HTMLButtonElement>) {
|
||||||
|
e.preventDefault()
|
||||||
|
setShowPricing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.pricing}>
|
||||||
|
<Formik
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
|
// move user's focus to top of screen
|
||||||
|
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||||
|
|
||||||
|
// Kick off price creation
|
||||||
|
await handleCreatePricing(values)
|
||||||
|
setSubmitting(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasFeedback ? (
|
||||||
|
<Feedback success={success} pricingStepText={pricingStepText} />
|
||||||
|
) : showPricing ? (
|
||||||
|
<FormPricing
|
||||||
|
ddo={ddo}
|
||||||
|
setShowPricing={setShowPricing}
|
||||||
|
content={content}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Alert
|
||||||
|
state="info"
|
||||||
|
title={content.empty.title}
|
||||||
|
text={content.empty.info}
|
||||||
|
action={{
|
||||||
|
name: content.empty.action.name,
|
||||||
|
handleAction: handleShowPricingForm
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -5,6 +5,10 @@
|
|||||||
margin-top: -1.5rem;
|
margin-top: -1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid > div {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
composes: box from '../../atoms/Box.module.css';
|
composes: box from '../../atoms/Box.module.css';
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
@ -12,7 +16,7 @@
|
|||||||
|
|
||||||
@media (min-width: 60rem) {
|
@media (min-width: 60rem) {
|
||||||
.grid {
|
.grid {
|
||||||
grid-template-columns: 1.5fr minmax(0, 1fr);
|
grid-template-columns: 1.5fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sticky {
|
.sticky {
|
||||||
|
@ -9,6 +9,8 @@ import styles from './index.module.css'
|
|||||||
import AssetActions from '../AssetActions'
|
import AssetActions from '../AssetActions'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
|
import Pricing from './Pricing'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
|
||||||
export interface AssetContentProps {
|
export interface AssetContentProps {
|
||||||
metadata: MetadataMarket
|
metadata: MetadataMarket
|
||||||
@ -22,9 +24,17 @@ export default function AssetContent({
|
|||||||
}: AssetContentProps): ReactElement {
|
}: AssetContentProps): ReactElement {
|
||||||
const { datePublished } = metadata.main
|
const { datePublished } = metadata.main
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
|
const { accountId } = useOcean()
|
||||||
|
|
||||||
|
const isOwner = accountId === ddo.publicKey[0].owner
|
||||||
|
const hasNoPrice = ddo.price.type === ''
|
||||||
|
const showPricing = isOwner && hasNoPrice
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.grid}>
|
<article className={styles.grid}>
|
||||||
|
<div>
|
||||||
|
{showPricing && <Pricing ddo={ddo} />}
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<aside className={styles.meta}>
|
<aside className={styles.meta}>
|
||||||
<p>{datePublished && <Time date={datePublished} />}</p>
|
<p>{datePublished && <Time date={datePublished} />}</p>
|
||||||
@ -61,6 +71,8 @@ export default function AssetContent({
|
|||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.sticky}>
|
<div className={styles.sticky}>
|
||||||
<AssetActions ddo={ddo} />
|
<AssetActions ddo={ddo} />
|
||||||
|
@ -3,28 +3,43 @@ import { MetadataPublishForm } from '../../../@types/MetaData'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { transformPublishFormToMetadata } from './utils'
|
import { transformPublishFormToMetadata } from './utils'
|
||||||
|
|
||||||
|
const Output = ({ title, output }: { title: string; output: any }) => (
|
||||||
|
<div>
|
||||||
|
<h5>{title}</h5>
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(output, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
export default function Debug({
|
export default function Debug({
|
||||||
values
|
values
|
||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishForm>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const ddo = {
|
||||||
|
'@context': 'https://w3id.org/did/v1',
|
||||||
|
dataTokenInfo: {
|
||||||
|
...values.dataTokenOptions
|
||||||
|
},
|
||||||
|
service: [
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
type: 'metadata',
|
||||||
|
attributes: { ...transformPublishFormToMetadata(values) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
type: values.access,
|
||||||
|
attributes: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<div>
|
<Output title="Collected Form Values" output={values} />
|
||||||
<h5>Collected Form Values</h5>
|
<Output title="Transformed DDO Values" output={ddo} />
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify(values, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h5>Transformed Values</h5>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{JSON.stringify(transformPublishFormToMetadata(values), null, 2)}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,18 @@ import Loader from '../../atoms/Loader'
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Feedback.module.css'
|
import styles from './Feedback.module.css'
|
||||||
import SuccessConfetti from '../../atoms/SuccessConfetti'
|
import SuccessConfetti from '../../atoms/SuccessConfetti'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export default function Feedback({
|
export default function Feedback({
|
||||||
error,
|
error,
|
||||||
success,
|
success,
|
||||||
did,
|
ddo,
|
||||||
publishStepText,
|
publishStepText,
|
||||||
setError
|
setError
|
||||||
}: {
|
}: {
|
||||||
error: string
|
error: string
|
||||||
success: string
|
success: string
|
||||||
did: string
|
ddo: DDO
|
||||||
publishStepText: string
|
publishStepText: string
|
||||||
setError: (error: string) => void
|
setError: (error: string) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
@ -22,7 +23,7 @@ export default function Feedback({
|
|||||||
<Button
|
<Button
|
||||||
style="primary"
|
style="primary"
|
||||||
size="small"
|
size="small"
|
||||||
href={`/asset/${did}`}
|
to={`/asset/${ddo?.id}`}
|
||||||
className={styles.action}
|
className={styles.action}
|
||||||
>
|
>
|
||||||
Go to data set →
|
Go to data set →
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { ReactElement, useEffect, FormEvent } from 'react'
|
import React, { ReactElement, useEffect, FormEvent } from 'react'
|
||||||
import styles from './PublishForm.module.css'
|
import styles from './FormPublish.module.css'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import { useFormikContext, Field } from 'formik'
|
import { useFormikContext, Field, Form } from 'formik'
|
||||||
import Input from '../../atoms/Input'
|
import Input from '../../atoms/Input'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||||
|
|
||||||
export default function PublishForm({
|
export default function FormPublish({
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
content: FormContent
|
content: FormContent
|
||||||
@ -37,7 +37,7 @@ export default function PublishForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<Form
|
||||||
className={styles.form}
|
className={styles.form}
|
||||||
// do we need this?
|
// do we need this?
|
||||||
onChange={() => status === 'empty' && setStatus(null)}
|
onChange={() => status === 'empty' && setStatus(null)}
|
||||||
@ -61,6 +61,6 @@ export default function PublishForm({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -44,11 +44,3 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.price {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price:only-child {
|
|
||||||
margin-right: -100%;
|
|
||||||
}
|
|
||||||
|
@ -7,8 +7,6 @@ import styles from './Preview.module.css'
|
|||||||
import File from '../../atoms/File'
|
import File from '../../atoms/File'
|
||||||
import { MetadataPublishForm } from '../../../@types/MetaData'
|
import { MetadataPublishForm } from '../../../@types/MetaData'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
import Conversion from '../../atoms/Price/Conversion'
|
|
||||||
import PriceUnit from '../../atoms/Price/PriceUnit'
|
|
||||||
|
|
||||||
export default function Preview({
|
export default function Preview({
|
||||||
values
|
values
|
||||||
@ -60,25 +58,6 @@ export default function Preview({
|
|||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{values.price && (
|
|
||||||
<div className={styles.price}>
|
|
||||||
<MetaItem
|
|
||||||
title={`Price: ${values.price.type}`}
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<PriceUnit
|
|
||||||
price="1"
|
|
||||||
symbol={values.price.datatoken?.symbol}
|
|
||||||
small
|
|
||||||
/>{' '}
|
|
||||||
= <PriceUnit price={`${values.price.price}`} small />
|
|
||||||
<Conversion price={`${values.price.price}`} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{typeof values.links !== 'string' && values.links?.length && (
|
{typeof values.links !== 'string' && values.links?.length && (
|
||||||
@ -107,7 +86,7 @@ export default function Preview({
|
|||||||
key.includes('files') ||
|
key.includes('files') ||
|
||||||
key.includes('links') ||
|
key.includes('links') ||
|
||||||
key.includes('termsAndConditions') ||
|
key.includes('termsAndConditions') ||
|
||||||
key.includes('price') ||
|
key.includes('dataTokenOptions') ||
|
||||||
value === undefined ||
|
value === undefined ||
|
||||||
value === ''
|
value === ''
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ import React, { ReactElement, useState } from 'react'
|
|||||||
import { Formik } from 'formik'
|
import { Formik } from 'formik'
|
||||||
import { usePublish } from '@oceanprotocol/react'
|
import { usePublish } from '@oceanprotocol/react'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import PublishForm from './PublishForm'
|
import FormPublish from './FormPublish'
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
||||||
import { FormContent } from '../../../@types/Form'
|
import { FormContent } from '../../../@types/Form'
|
||||||
import { initialValues, validationSchema } from '../../../models/FormPublish'
|
import { initialValues, validationSchema } from '../../../models/FormPublish'
|
||||||
@ -10,7 +10,7 @@ import { transformPublishFormToMetadata } from './utils'
|
|||||||
import Preview from './Preview'
|
import Preview from './Preview'
|
||||||
import { MetadataPublishForm } from '../../../@types/MetaData'
|
import { MetadataPublishForm } from '../../../@types/MetaData'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
import { Logger, Metadata } from '@oceanprotocol/lib'
|
import { DDO, Logger, Metadata } from '@oceanprotocol/lib'
|
||||||
import { Persist } from '../../atoms/FormikPersist'
|
import { Persist } from '../../atoms/FormikPersist'
|
||||||
import Debug from './Debug'
|
import Debug from './Debug'
|
||||||
import Feedback from './Feedback'
|
import Feedback from './Feedback'
|
||||||
@ -27,7 +27,7 @@ export default function PublishPage({
|
|||||||
|
|
||||||
const [success, setSuccess] = useState<string>()
|
const [success, setSuccess] = useState<string>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const [did, setDid] = useState<string>()
|
const [ddo, setDdo] = useState<DDO>()
|
||||||
|
|
||||||
const hasFeedback = isLoading || error || success
|
const hasFeedback = isLoading || error || success
|
||||||
|
|
||||||
@ -36,33 +36,35 @@ export default function PublishPage({
|
|||||||
resetForm: () => void
|
resetForm: () => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const metadata = transformPublishFormToMetadata(values)
|
const metadata = transformPublishFormToMetadata(values)
|
||||||
const { price } = values
|
|
||||||
const serviceType = values.access === 'Download' ? 'access' : 'compute'
|
const serviceType = values.access === 'Download' ? 'access' : 'compute'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Logger.log('Publish with ', price, serviceType, price.datatoken)
|
Logger.log(
|
||||||
|
'Publish with ',
|
||||||
|
metadata,
|
||||||
|
serviceType,
|
||||||
|
values.dataTokenOptions
|
||||||
|
)
|
||||||
|
|
||||||
const ddo = await publish(
|
const ddo = await publish(
|
||||||
(metadata as unknown) as Metadata,
|
(metadata as unknown) as Metadata,
|
||||||
// swapFee is tricky: to get 0.1% you need to send 0.001 as value
|
|
||||||
{ ...price, swapFee: `${price.swapFee / 100}` },
|
|
||||||
serviceType,
|
serviceType,
|
||||||
price.datatoken
|
values.dataTokenOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
// Publish failed
|
// Publish failed
|
||||||
if (publishError) {
|
if (!ddo || publishError) {
|
||||||
setError(publishError)
|
setError(publishError || 'Publishing DDO failed.')
|
||||||
Logger.error(publishError)
|
Logger.error(publishError || 'Publishing DDO failed.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish succeeded
|
// Publish succeeded
|
||||||
if (ddo) {
|
setDdo(ddo)
|
||||||
setDid(ddo.id)
|
setSuccess(
|
||||||
setSuccess('🎉 Successfully published your data set. 🎉')
|
'🎉 Successfully published. 🎉 Now create a price on your data set.'
|
||||||
|
)
|
||||||
resetForm()
|
resetForm()
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error.message)
|
setError(error.message)
|
||||||
Logger.error(error.message)
|
Logger.error(error.message)
|
||||||
@ -91,12 +93,12 @@ export default function PublishPage({
|
|||||||
error={error}
|
error={error}
|
||||||
success={success}
|
success={success}
|
||||||
publishStepText={publishStepText}
|
publishStepText={publishStepText}
|
||||||
did={did}
|
ddo={ddo}
|
||||||
setError={setError}
|
setError={setError}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<article className={styles.grid}>
|
<article className={styles.grid}>
|
||||||
<PublishForm content={content.form} />
|
<FormPublish content={content.form} />
|
||||||
<aside>
|
<aside>
|
||||||
<div className={styles.sticky}>
|
<div className={styles.sticky}>
|
||||||
<Preview values={values} />
|
<Preview values={values} />
|
||||||
|
@ -16,8 +16,7 @@ export function transformPublishFormToMetadata(
|
|||||||
tags,
|
tags,
|
||||||
links,
|
links,
|
||||||
termsAndConditions,
|
termsAndConditions,
|
||||||
files,
|
files
|
||||||
price
|
|
||||||
} = data
|
} = data
|
||||||
|
|
||||||
const metadata: MetadataMarket = {
|
const metadata: MetadataMarket = {
|
||||||
@ -36,8 +35,7 @@ export function transformPublishFormToMetadata(
|
|||||||
copyrightHolder,
|
copyrightHolder,
|
||||||
tags: tags?.split(','),
|
tags: tags?.split(','),
|
||||||
links: typeof links !== 'string' && links,
|
links: typeof links !== 'string' && links,
|
||||||
termsAndConditions,
|
termsAndConditions
|
||||||
priceType: price.type
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +23,17 @@ export default function PageTemplateAssetDetails({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function init() {
|
async function init() {
|
||||||
|
if (ddo) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const metadataCache = new MetadataCache(config.metadataCacheUri, Logger)
|
const metadataCache = new MetadataCache(config.metadataCacheUri, Logger)
|
||||||
const ddo = await metadataCache.retrieveDDO(did)
|
const ddo = await metadataCache.retrieveDDO(did)
|
||||||
|
|
||||||
if (!ddo) {
|
if (!ddo) {
|
||||||
setTitle('Could not retrieve asset')
|
setTitle('Could not retrieve asset')
|
||||||
setError('The DDO was not found in MetadataCache.')
|
setError(
|
||||||
|
`The DDO for ${did} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.`
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +48,11 @@ export default function PageTemplateAssetDetails({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
}, [did, config.metadataCacheUri])
|
|
||||||
|
// Periodically try to get DDO when not present yet
|
||||||
|
const timer = !ddo && setInterval(() => init(), 2000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}, [ddo, did, config.metadataCacheUri])
|
||||||
|
|
||||||
return did && metadata ? (
|
return did && metadata ? (
|
||||||
<Layout title={title} uri={uri}>
|
<Layout title={title} uri={uri}>
|
||||||
|
@ -18,8 +18,7 @@ const AssetModel: MetadataMarket = {
|
|||||||
links: undefined,
|
links: undefined,
|
||||||
|
|
||||||
// custom items
|
// custom items
|
||||||
termsAndConditions: false,
|
termsAndConditions: false
|
||||||
priceType: undefined
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
src/models/FormPricing.ts
Normal file
24
src/models/FormPricing.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { PriceOptionsMarket } from '../@types/MetaData'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const validationSchema = Yup.object().shape<PriceOptionsMarket>({
|
||||||
|
price: Yup.number().min(1, 'Must be greater than 0').required('Required'),
|
||||||
|
dtAmount: Yup.number().min(1, 'Must be greater than 0').required('Required'),
|
||||||
|
type: Yup.string()
|
||||||
|
.matches(/fixed|dynamic/g)
|
||||||
|
.required('Required'),
|
||||||
|
weightOnDataToken: Yup.string().required('Required'),
|
||||||
|
swapFee: Yup.number()
|
||||||
|
.min(0.1, 'Must be more or equal to 0.1')
|
||||||
|
.max(10, 'Maximum is 10%')
|
||||||
|
.required('Required')
|
||||||
|
.nullable()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const initialValues: PriceOptionsMarket = {
|
||||||
|
price: 1,
|
||||||
|
type: 'dynamic',
|
||||||
|
dtAmount: 1,
|
||||||
|
weightOnDataToken: '9', // 90% on data token
|
||||||
|
swapFee: 0.1 // in %
|
||||||
|
}
|
@ -6,27 +6,11 @@ export const validationSchema = Yup.object().shape<MetadataPublishForm>({
|
|||||||
// ---- required fields ----
|
// ---- required fields ----
|
||||||
name: Yup.string().required('Required'),
|
name: Yup.string().required('Required'),
|
||||||
author: Yup.string().required('Required'),
|
author: Yup.string().required('Required'),
|
||||||
price: Yup.object()
|
dataTokenOptions: Yup.object()
|
||||||
.shape({
|
|
||||||
price: Yup.number().min(1, 'Must be greater than 0').required('Required'),
|
|
||||||
tokensToMint: Yup.number()
|
|
||||||
.min(1, 'Must be greater than 0')
|
|
||||||
.required('Required'),
|
|
||||||
type: Yup.string()
|
|
||||||
.matches(/fixed|dynamic/g)
|
|
||||||
.required('Required'),
|
|
||||||
weightOnDataToken: Yup.string().required('Required'),
|
|
||||||
swapFee: Yup.number()
|
|
||||||
.min(0.1, 'Must be more or equal to 0.1')
|
|
||||||
.max(0.9, 'Must be less or equal to 0.9')
|
|
||||||
.required('Required'),
|
|
||||||
datatoken: Yup.object()
|
|
||||||
.shape({
|
.shape({
|
||||||
name: Yup.string(),
|
name: Yup.string(),
|
||||||
symbol: Yup.string()
|
symbol: Yup.string()
|
||||||
})
|
})
|
||||||
.nullable()
|
|
||||||
})
|
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
||||||
description: Yup.string().required('Required'),
|
description: Yup.string().required('Required'),
|
||||||
@ -45,12 +29,9 @@ export const validationSchema = Yup.object().shape<MetadataPublishForm>({
|
|||||||
export const initialValues: Partial<MetadataPublishForm> = {
|
export const initialValues: Partial<MetadataPublishForm> = {
|
||||||
name: '',
|
name: '',
|
||||||
author: '',
|
author: '',
|
||||||
price: {
|
dataTokenOptions: {
|
||||||
price: 1,
|
name: '',
|
||||||
type: 'dynamic',
|
symbol: ''
|
||||||
tokensToMint: 1,
|
|
||||||
weightOnDataToken: '9', // 90% on data token
|
|
||||||
swapFee: 0.1 // in %
|
|
||||||
},
|
},
|
||||||
files: '',
|
files: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
@ -4,12 +4,9 @@ const testFormData: MetadataPublishForm = {
|
|||||||
author: '',
|
author: '',
|
||||||
files: [],
|
files: [],
|
||||||
license: '',
|
license: '',
|
||||||
price: {
|
dataTokenOptions: {
|
||||||
price: 1,
|
name: '',
|
||||||
tokensToMint: 9,
|
symbol: ''
|
||||||
type: 'fixed',
|
|
||||||
weightOnDataToken: '1',
|
|
||||||
swapFee: 0.1
|
|
||||||
},
|
},
|
||||||
name: '',
|
name: '',
|
||||||
description: 'description',
|
description: 'description',
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
MetadataMarket,
|
MetadataMarket,
|
||||||
MetadataPublishForm
|
MetadataPublishForm
|
||||||
} from '../../../src/@types/MetaData'
|
} from '../../../src/@types/MetaData'
|
||||||
import PublishForm from '../../../src/components/pages/Publish/PublishForm'
|
import PublishForm from '../../../src/components/pages/Publish/FormPublish'
|
||||||
import publishFormData from '../__fixtures__/testFormData'
|
import publishFormData from '../__fixtures__/testFormData'
|
||||||
import content from '../../../content/pages/publish.json'
|
import content from '../../../content/pages/publish.json'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user