1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

split up publish, datatoken, pricing data

This commit is contained in:
Matthias Kretschmann 2020-10-19 14:47:11 +02:00
parent 28e5a06b60
commit 95e72ae108
Signed by: m
GPG Key ID: 606EEEF3C479A91F
18 changed files with 147 additions and 124 deletions

View File

@ -42,9 +42,9 @@
"required": true "required": true
}, },
{ {
"name": "price", "name": "dataTokenOptions",
"label": "Price", "label": "Datatoken",
"type": "price", "type": "datatoken",
"required": true "required": true
}, },
{ {

View File

@ -4,7 +4,7 @@ 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[]
@ -22,8 +22,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 +31,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 ----

View File

@ -5,6 +5,7 @@ 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 Price from '../../molecules/FormFields/Price'
import Datatoken from '../../molecules/FormFields/Datatoken'
const DefaultInput = ({ const DefaultInput = ({
small, small,
@ -89,6 +90,8 @@ export default function InputElement({
return <FilesInput name={name} {...field} {...props} /> return <FilesInput name={name} {...field} {...props} />
case 'price': case 'price':
return <Price name={name} {...field} {...props} /> return <Price name={name} {...field} {...props} />
case 'datatoken':
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:

View 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, but only once automatically
useEffect(() => {
if (!ocean || typeof field?.value?.name !== 'undefined') return
generateName()
}, [ocean])
return (
<div className={styles.datatoken}>
<strong>{field?.value?.name}</strong> {' '}
<strong>{field?.value?.symbol}</strong>
<RefreshName generateName={generateName} />
</div>
)
}

View File

@ -5,7 +5,6 @@ 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}>

View File

@ -17,13 +17,11 @@ export default function Dynamic({
ocean, ocean,
priceOptions, priceOptions,
datatokenOptions, datatokenOptions,
generateName,
content content
}: { }: {
ocean: number ocean: number
priceOptions: PriceOptionsMarket priceOptions: PriceOptionsMarket
datatokenOptions: DataTokenOptions datatokenOptions: DataTokenOptions
generateName: () => void
content: any content: any
}): ReactElement { }): ReactElement {
const { appConfig } = useSiteMetadata() const { appConfig } = useSiteMetadata()
@ -91,7 +89,6 @@ export default function Dynamic({
name="price.tokensToMint" name="price.tokensToMint"
datatokenOptions={datatokenOptions} datatokenOptions={datatokenOptions}
weight={`${Number(weightOnDataToken) * 10}%`} weight={`${Number(weightOnDataToken) * 10}%`}
generateName={generateName}
readOnly readOnly
/> />
</div> </div>

View File

@ -3,21 +3,11 @@ 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'
export default function Fixed({ export default function Fixed({ content }: { content: any }): ReactElement {
datatokenOptions,
generateName,
content
}: {
datatokenOptions: DataTokenOptions
generateName: () => void
content: any
}): ReactElement {
const [field, meta] = useField('price.price') const [field, meta] = useField('price.price')
return ( return (
@ -43,16 +33,6 @@ export default function Fixed({
/> />
<Error meta={meta} /> <Error meta={meta} />
</div> </div>
{datatokenOptions && (
<div className={styles.datatoken}>
<h4>
Data Token <RefreshName generateName={generateName} />
</h4>
<strong>{datatokenOptions?.name}</strong> {' '}
<strong>{datatokenOptions?.symbol}</strong>
</div>
)}
</div> </div>
</div> </div>
) )

View File

@ -5,9 +5,8 @@ import styles from './index.module.css'
import Tabs from '../../../atoms/Tabs' import Tabs from '../../../atoms/Tabs'
import Fixed from './Fixed' import Fixed from './Fixed'
import Dynamic from './Dynamic' import Dynamic from './Dynamic'
import { useField, useFormikContext } from 'formik' import { useField } from 'formik'
import { useUserPreferences } from '../../../../providers/UserPreferences' import { useUserPreferences } from '../../../../providers/UserPreferences'
import { useOcean } from '@oceanprotocol/react'
import { PriceOptionsMarket } from '../../../../@types/MetaData' import { PriceOptionsMarket } from '../../../../@types/MetaData'
const query = graphql` const query = graphql`
@ -43,7 +42,6 @@ export default function Price(props: InputProps): ReactElement {
const { debug } = useUserPreferences() const { debug } = useUserPreferences()
const data = useStaticQuery(query) const data = useStaticQuery(query)
const content = data.content.edges[0].node.childPagesJson.price const content = data.content.edges[0].node.childPagesJson.price
const { ocean } = useOcean()
const [field, meta, helpers] = useField(props.name) const [field, meta, helpers] = useField(props.name)
const { price }: PriceOptionsMarket = field.value const { price }: PriceOptionsMarket = field.value
@ -55,12 +53,6 @@ export default function Price(props: InputProps): ReactElement {
helpers.setValue({ ...field.value, type }) 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 // Always update everything when amountOcean changes
useEffect(() => { useEffect(() => {
const tokensToMint = Number(price) * Number(field.value.weightOnDataToken) const tokensToMint = Number(price) * Number(field.value.weightOnDataToken)
@ -68,22 +60,10 @@ export default function Price(props: InputProps): ReactElement {
helpers.setValue({ ...field.value, tokensToMint }) helpers.setValue({ ...field.value, tokensToMint })
}, [price]) }, [price])
// Generate new DT name & symbol, but only once automatically
useEffect(() => {
if (!ocean || typeof field?.value?.datatoken?.name !== 'undefined') return
generateName()
}, [ocean])
const tabs = [ const tabs = [
{ {
title: content.fixed.title, title: content.fixed.title,
content: ( content: <Fixed content={content.fixed} />
<Fixed
datatokenOptions={field.value.datatoken}
generateName={generateName}
content={content.fixed}
/>
)
}, },
{ {
title: content.dynamic.title, title: content.dynamic.title,
@ -92,7 +72,6 @@ export default function Price(props: InputProps): ReactElement {
ocean={price} ocean={price}
priceOptions={{ ...field.value, tokensToMint }} priceOptions={{ ...field.value, tokensToMint }}
datatokenOptions={field.value.datatoken} datatokenOptions={field.value.datatoken}
generateName={generateName}
content={content.dynamic} content={content.dynamic}
/> />
) )

View File

@ -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%;
}

View File

@ -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 === ''
) )

View File

@ -0,0 +1,47 @@
import React, { ReactElement, useState, useEffect } from 'react'
import { Field, FieldInputProps, Formik } from 'formik'
import Input from '../../atoms/Input'
import { FormPricing } from 'models/FormPricing'
export default function Pricing(): ReactElement {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={async (values, { setSubmitting, resetForm }) => {
await handlePricing(values.amount, resetForm)
setSubmitting(false)
}}
>
{({
values,
touched,
setTouched,
isSubmitting,
setFieldValue,
submitForm,
handleChange
}) => (
<>
<Field name="price">
{({
field,
form
}: {
field: FieldInputProps<FormPricing>
form: any
}) => (
<Input
type="price"
name="price"
label="Price"
field={field}
form={form}
/>
)}
</Field>
</>
)}
</Formik>
)
}

View File

@ -36,18 +36,20 @@ 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

View File

@ -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
} }
} }

41
src/models/FormPricing.ts Normal file
View File

@ -0,0 +1,41 @@
import { PriceOptionsMarket } from '../@types/MetaData'
import * as Yup from 'yup'
export interface FormPricing {
price: PriceOptionsMarket
}
export const validationSchema = Yup.object().shape<FormPricing>({
price: 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({
name: Yup.string(),
symbol: Yup.string()
})
.nullable()
})
.required('Required')
})
export const initialValues: Partial<FormPricing> = {
price: {
price: 1,
type: 'dynamic',
tokensToMint: 1,
weightOnDataToken: '9', // 90% on data token
swapFee: 0.1 // in %
}
}

View File

@ -6,26 +6,10 @@ 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({ .shape({
price: Yup.number().min(1, 'Must be greater than 0').required('Required'), name: Yup.string(),
tokensToMint: Yup.number() symbol: Yup.string()
.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({
name: Yup.string(),
symbol: Yup.string()
})
.nullable()
}) })
.required('Required'), .required('Required'),
files: Yup.array<FileMetadata>().required('Required').nullable(), files: Yup.array<FileMetadata>().required('Required').nullable(),
@ -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: '',