From be9fde8827fe321dee98c22aae509c67ab6547cb Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 19 Oct 2020 11:33:06 +0200 Subject: [PATCH 01/32] get datatoken balance for use in consume/compute --- .../organisms/AssetActions/Compute.tsx | 4 ++- .../organisms/AssetActions/Consume.tsx | 22 +++++++------- .../organisms/AssetActions/index.tsx | 30 +++++++++++++++++-- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/components/organisms/AssetActions/Compute.tsx b/src/components/organisms/AssetActions/Compute.tsx index 93b3d0cb0..2768e2418 100644 --- a/src/components/organisms/AssetActions/Compute.tsx +++ b/src/components/organisms/AssetActions/Compute.tsx @@ -19,10 +19,12 @@ import { useSiteMetadata } from '../../../hooks/useSiteMetadata' export default function Compute({ ddo, - isBalanceSufficient + isBalanceSufficient, + dtBalance }: { ddo: DDO isBalanceSufficient: boolean + dtBalance: string }): ReactElement { const { ocean } = useOcean() const { compute, isLoading, computeStepText, computeError } = useCompute() diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 275632f23..5a6029f4d 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useEffect } from 'react' import { toast } from 'react-toastify' import { File as FileMetadata, DDO } from '@oceanprotocol/lib' import Button from '../../atoms/Button' @@ -13,11 +13,13 @@ import { useSiteMetadata } from '../../../hooks/useSiteMetadata' export default function Consume({ ddo, file, - isBalanceSufficient + isBalanceSufficient, + dtBalance }: { ddo: DDO file: FileMetadata isBalanceSufficient: boolean + dtBalance: string }): ReactElement { const { ocean } = useOcean() const { marketFeeAddress } = useSiteMetadata() @@ -25,22 +27,20 @@ export default function Consume({ const isDisabled = !ocean || !isBalanceSufficient - if (consumeError) { - toast.error(consumeError) + async function handleConsume() { + await consume(ddo.id, ddo.dataToken, 'access', marketFeeAddress) } + useEffect(() => { + consumeError && toast.error(consumeError) + }, [consumeError]) + const PurchaseButton = () => (
{consumeStepText ? ( ) : ( - )} diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx index a561aca69..4527acf71 100644 --- a/src/components/organisms/AssetActions/index.tsx +++ b/src/components/organisms/AssetActions/index.tsx @@ -2,7 +2,7 @@ import React, { ReactElement, useState, useEffect } from 'react' import styles from './index.module.css' import Compute from './Compute' import Consume from './Consume' -import { DDO } from '@oceanprotocol/lib' +import { DDO, Logger } from '@oceanprotocol/lib' import Tabs from '../../atoms/Tabs' import { useOcean, useMetadata } from '@oceanprotocol/react' import compareAsBN from '../../../utils/compareAsBN' @@ -10,13 +10,32 @@ import Pool from './Pool' import { AdditionalInformationMarket } from '../../../@types/MetaData' export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement { - const { balance } = useOcean() + const { ocean, balance, accountId } = useOcean() const { price } = useMetadata(ddo) const [isBalanceSufficient, setIsBalanceSufficient] = useState() + const [dtBalance, setDtBalance] = useState() const isCompute = Boolean(ddo.findServiceByType('compute')) 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 useEffect(() => { if (!price || !price.value || !balance || !balance.ocean) return @@ -29,10 +48,15 @@ export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement { }, [balance, price]) const UseContent = isCompute ? ( - + ) : ( From a5815669c7a99d05b86dee8de73b97ce2558b923 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 19 Oct 2020 12:14:57 +0200 Subject: [PATCH 02/32] refactor consume --- .../organisms/AssetActions/Consume.module.css | 6 +++++ .../organisms/AssetActions/Consume.tsx | 24 ++++++++++++++----- .../organisms/AssetActions/index.tsx | 9 ++++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/components/organisms/AssetActions/Consume.module.css b/src/components/organisms/AssetActions/Consume.module.css index b400962b9..9b03d5d16 100644 --- a/src/components/organisms/AssetActions/Consume.module.css +++ b/src/components/organisms/AssetActions/Consume.module.css @@ -19,3 +19,9 @@ .feedback { width: 100%; } + +.hasTokens { + font-size: var(--font-size-small); + color: var(--color-secondary); + margin-top: calc(var(--spacer) / 4); +} diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 5a6029f4d..541a69397 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -7,7 +7,7 @@ import Price from '../../atoms/Price' import Web3Feedback from '../../molecules/Wallet/Feedback' import styles from './Consume.module.css' import Loader from '../../atoms/Loader' -import { useOcean, useConsume } from '@oceanprotocol/react' +import { useOcean, useConsume, usePricing } from '@oceanprotocol/react' import { useSiteMetadata } from '../../../hooks/useSiteMetadata' export default function Consume({ @@ -23,25 +23,31 @@ export default function Consume({ }): ReactElement { const { ocean } = useOcean() const { marketFeeAddress } = useSiteMetadata() + const { buyDT, pricingStepText, pricingError } = usePricing() const { consumeStepText, consume, consumeError } = useConsume() - const isDisabled = !ocean || !isBalanceSufficient + const isDisabled = + !ocean || !isBalanceSufficient || consumeStepText || pricingStepText + const hasDatatoken = Number(dtBalance) >= 1 async function handleConsume() { + !hasDatatoken && (await buyDT(ddo.dataToken, '1')) await consume(ddo.id, ddo.dataToken, 'access', marketFeeAddress) } + // Output errors in UI useEffect(() => { consumeError && toast.error(consumeError) - }, [consumeError]) + pricingError && toast.error(pricingError) + }, [consumeError, pricingError]) const PurchaseButton = () => (
- {consumeStepText ? ( - + {consumeStepText || pricingStepText ? ( + ) : ( )}
@@ -55,6 +61,12 @@ export default function Consume({
+ {hasDatatoken && ( +
+ You own {dtBalance} {ddo.dataTokenInfo?.symbol || 'DT'} so you can + use this asset without paying again. +
+ )}
diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx index 4527acf71..ca2fedff0 100644 --- a/src/components/organisms/AssetActions/index.tsx +++ b/src/components/organisms/AssetActions/index.tsx @@ -38,14 +38,17 @@ export default function AssetActions({ ddo }: { ddo: DDO }): ReactElement { // Check user balance against price 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 () => { setIsBalanceSufficient(false) } - }, [balance, price]) + }, [balance, price, dtBalance]) const UseContent = isCompute ? ( Date: Mon, 19 Oct 2020 12:54:30 +0200 Subject: [PATCH 03/32] more consume refactor --- src/components/atoms/Loader.module.css | 6 +++--- .../organisms/AssetActions/Consume.module.css | 7 ++----- .../organisms/AssetActions/Consume.tsx | 20 ++++++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/components/atoms/Loader.module.css b/src/components/atoms/Loader.module.css index 8b09217ca..a678a7811 100644 --- a/src/components/atoms/Loader.module.css +++ b/src/components/atoms/Loader.module.css @@ -1,12 +1,12 @@ .loaderWrap { display: flex; - align-items: center; } .loader { display: block; - width: 20px; - height: 20px; + flex: 0 0 1.2rem; + width: 1.2rem; + height: 1.2rem; border-radius: 50%; border: 2px solid var(--brand-purple); border-top-color: var(--brand-violet); diff --git a/src/components/organisms/AssetActions/Consume.module.css b/src/components/organisms/AssetActions/Consume.module.css index 9b03d5d16..bf99d0d80 100644 --- a/src/components/organisms/AssetActions/Consume.module.css +++ b/src/components/organisms/AssetActions/Consume.module.css @@ -12,12 +12,9 @@ flex-shrink: 0; } -.pricewrapper button { - margin-top: var(--spacer); -} - -.feedback { +.actions { width: 100%; + margin-top: calc(var(--spacer) / 2); } .hasTokens { diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 541a69397..6119e2cfb 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -23,15 +23,21 @@ export default function Consume({ }): ReactElement { const { ocean } = useOcean() const { marketFeeAddress } = useSiteMetadata() - const { buyDT, pricingStepText, pricingError } = usePricing() + const { + dtSymbol, + buyDT, + pricingStepText, + pricingError, + pricingIsLoading + } = usePricing(ddo) const { consumeStepText, consume, consumeError } = useConsume() const isDisabled = - !ocean || !isBalanceSufficient || consumeStepText || pricingStepText + !ocean || !isBalanceSufficient || consumeStepText || pricingIsLoading const hasDatatoken = Number(dtBalance) >= 1 async function handleConsume() { - !hasDatatoken && (await buyDT(ddo.dataToken, '1')) + !hasDatatoken && (await buyDT('1')) await consume(ddo.id, ddo.dataToken, 'access', marketFeeAddress) } @@ -42,8 +48,8 @@ export default function Consume({ }, [consumeError, pricingError]) const PurchaseButton = () => ( -
- {consumeStepText || pricingStepText ? ( +
+ {consumeStepText || pricingIsLoading ? ( ) : (
+ {hasDatatoken && ( +
+ You own {dtBalance} {dtSymbol} so you can use this asset without + paying again. +
+ )}
From 95e72ae108aa7c22a8056fc59ba08be55130bf47 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 19 Oct 2020 14:47:11 +0200 Subject: [PATCH 05/32] split up publish, datatoken, pricing data --- content/pages/publish.json | 6 +-- src/@types/MetaData.d.ts | 6 +-- src/components/atoms/Input/InputElement.tsx | 3 ++ .../RefreshName.module.css | 0 .../{Price => Datatoken}/RefreshName.tsx | 0 .../FormFields/Datatoken/index.module.css | 0 .../molecules/FormFields/Datatoken/index.tsx | 32 +++++++++++++ .../molecules/FormFields/Price/Coin.tsx | 6 --- .../molecules/FormFields/Price/Dynamic.tsx | 3 -- .../molecules/FormFields/Price/Fixed.tsx | 22 +-------- .../molecules/FormFields/Price/index.tsx | 25 +--------- .../pages/Publish/Preview.module.css | 8 ---- src/components/pages/Publish/Preview.tsx | 23 +-------- src/components/pages/Publish/Pricing.tsx | 47 +++++++++++++++++++ src/components/pages/Publish/index.tsx | 12 +++-- src/components/pages/Publish/utils.ts | 6 +-- src/models/FormPricing.ts | 41 ++++++++++++++++ src/models/FormPublish.ts | 31 +++--------- 18 files changed, 147 insertions(+), 124 deletions(-) rename src/components/molecules/FormFields/{Price => Datatoken}/RefreshName.module.css (100%) rename src/components/molecules/FormFields/{Price => Datatoken}/RefreshName.tsx (100%) create mode 100644 src/components/molecules/FormFields/Datatoken/index.module.css create mode 100644 src/components/molecules/FormFields/Datatoken/index.tsx create mode 100644 src/components/pages/Publish/Pricing.tsx create mode 100644 src/models/FormPricing.ts diff --git a/content/pages/publish.json b/content/pages/publish.json index 4d19ce927..14178508c 100644 --- a/content/pages/publish.json +++ b/content/pages/publish.json @@ -42,9 +42,9 @@ "required": true }, { - "name": "price", - "label": "Price", - "type": "price", + "name": "dataTokenOptions", + "label": "Datatoken", + "type": "datatoken", "required": true }, { diff --git a/src/@types/MetaData.d.ts b/src/@types/MetaData.d.ts index 4dd9189da..b207c47bc 100644 --- a/src/@types/MetaData.d.ts +++ b/src/@types/MetaData.d.ts @@ -4,7 +4,7 @@ import { AdditionalInformation, ServiceMetadata } from '@oceanprotocol/lib' -import { PriceOptions, DataTokenOptions } from '@oceanprotocol/react' +import { DataTokenOptions, PriceOptions } from '@oceanprotocol/react' export interface AdditionalInformationMarket extends AdditionalInformation { links?: File[] @@ -22,8 +22,6 @@ export interface MetadataMarket extends Metadata { export interface PriceOptionsMarket extends PriceOptions { // easier to keep this as number for Yup input validation swapFee: number - // collect datatoken info for publishing - datatoken?: DataTokenOptions } export interface MetadataPublishForm { @@ -33,7 +31,7 @@ export interface MetadataPublishForm { files: string | File[] author: string license: string - price: PriceOptionsMarket + dataTokenOptions: DataTokenOptions access: 'Download' | 'Compute' | string termsAndConditions: boolean // ---- optional fields ---- diff --git a/src/components/atoms/Input/InputElement.tsx b/src/components/atoms/Input/InputElement.tsx index 61abd511e..05cd84459 100644 --- a/src/components/atoms/Input/InputElement.tsx +++ b/src/components/atoms/Input/InputElement.tsx @@ -5,6 +5,7 @@ import { InputProps } from '.' import FilesInput from '../../molecules/FormFields/FilesInput' import Terms from '../../molecules/FormFields/Terms' import Price from '../../molecules/FormFields/Price' +import Datatoken from '../../molecules/FormFields/Datatoken' const DefaultInput = ({ small, @@ -89,6 +90,8 @@ export default function InputElement({ return case 'price': return + case 'datatoken': + return case 'terms': return default: diff --git a/src/components/molecules/FormFields/Price/RefreshName.module.css b/src/components/molecules/FormFields/Datatoken/RefreshName.module.css similarity index 100% rename from src/components/molecules/FormFields/Price/RefreshName.module.css rename to src/components/molecules/FormFields/Datatoken/RefreshName.module.css diff --git a/src/components/molecules/FormFields/Price/RefreshName.tsx b/src/components/molecules/FormFields/Datatoken/RefreshName.tsx similarity index 100% rename from src/components/molecules/FormFields/Price/RefreshName.tsx rename to src/components/molecules/FormFields/Datatoken/RefreshName.tsx diff --git a/src/components/molecules/FormFields/Datatoken/index.module.css b/src/components/molecules/FormFields/Datatoken/index.module.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/molecules/FormFields/Datatoken/index.tsx b/src/components/molecules/FormFields/Datatoken/index.tsx new file mode 100644 index 000000000..2f25e54fe --- /dev/null +++ b/src/components/molecules/FormFields/Datatoken/index.tsx @@ -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 ( +
+ {field?.value?.name} —{' '} + {field?.value?.symbol} + +
+ ) +} diff --git a/src/components/molecules/FormFields/Price/Coin.tsx b/src/components/molecules/FormFields/Price/Coin.tsx index 0f1f299c2..667b5c16b 100644 --- a/src/components/molecules/FormFields/Price/Coin.tsx +++ b/src/components/molecules/FormFields/Price/Coin.tsx @@ -5,7 +5,6 @@ import InputElement from '../../../atoms/Input/InputElement' import { ReactComponent as Logo } from '../../../../images/logo.svg' import Conversion from '../../../atoms/Price/Conversion' import { DataTokenOptions } from '@oceanprotocol/react' -import RefreshName from './RefreshName' import { useField } from 'formik' import Error from './Error' @@ -13,13 +12,11 @@ export default function Coin({ datatokenOptions, name, weight, - generateName, readOnly }: { datatokenOptions: DataTokenOptions name: string weight: string - generateName?: () => void readOnly?: boolean }): ReactElement { const [field, meta] = useField(name) @@ -32,9 +29,6 @@ export default function Coin({

{datatokenOptions?.name || 'Data Token'} - {datatokenOptions?.name && typeof generateName === 'function' && ( - - )}

diff --git a/src/components/molecules/FormFields/Price/Dynamic.tsx b/src/components/molecules/FormFields/Price/Dynamic.tsx index 75b15da25..d5e322b07 100644 --- a/src/components/molecules/FormFields/Price/Dynamic.tsx +++ b/src/components/molecules/FormFields/Price/Dynamic.tsx @@ -17,13 +17,11 @@ export default function Dynamic({ ocean, priceOptions, datatokenOptions, - generateName, content }: { ocean: number priceOptions: PriceOptionsMarket datatokenOptions: DataTokenOptions - generateName: () => void content: any }): ReactElement { const { appConfig } = useSiteMetadata() @@ -91,7 +89,6 @@ export default function Dynamic({ name="price.tokensToMint" datatokenOptions={datatokenOptions} weight={`${Number(weightOnDataToken) * 10}%`} - generateName={generateName} readOnly />
diff --git a/src/components/molecules/FormFields/Price/Fixed.tsx b/src/components/molecules/FormFields/Price/Fixed.tsx index 4ae9454cc..8ec57ea25 100644 --- a/src/components/molecules/FormFields/Price/Fixed.tsx +++ b/src/components/molecules/FormFields/Price/Fixed.tsx @@ -3,21 +3,11 @@ import stylesIndex from './index.module.css' import styles from './Fixed.module.css' import FormHelp from '../../../atoms/Input/Help' import Conversion from '../../../atoms/Price/Conversion' -import { DataTokenOptions } from '@oceanprotocol/react' -import RefreshName from './RefreshName' import { useField } from 'formik' import Input from '../../../atoms/Input' import Error from './Error' -export default function Fixed({ - datatokenOptions, - generateName, - content -}: { - datatokenOptions: DataTokenOptions - generateName: () => void - content: any -}): ReactElement { +export default function Fixed({ content }: { content: any }): ReactElement { const [field, meta] = useField('price.price') return ( @@ -43,16 +33,6 @@ export default function Fixed({ /> - - {datatokenOptions && ( -
-

- Data Token -

- {datatokenOptions?.name} —{' '} - {datatokenOptions?.symbol} -
- )} ) diff --git a/src/components/molecules/FormFields/Price/index.tsx b/src/components/molecules/FormFields/Price/index.tsx index b4d41ae25..350fe768d 100644 --- a/src/components/molecules/FormFields/Price/index.tsx +++ b/src/components/molecules/FormFields/Price/index.tsx @@ -5,9 +5,8 @@ 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 { useField } from 'formik' import { useUserPreferences } from '../../../../providers/UserPreferences' -import { useOcean } from '@oceanprotocol/react' import { PriceOptionsMarket } from '../../../../@types/MetaData' const query = graphql` @@ -43,7 +42,6 @@ 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 @@ -55,12 +53,6 @@ export default function Price(props: InputProps): ReactElement { 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) @@ -68,22 +60,10 @@ export default function Price(props: InputProps): ReactElement { 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: ( - - ) + content: }, { title: content.dynamic.title, @@ -92,7 +72,6 @@ export default function Price(props: InputProps): ReactElement { ocean={price} priceOptions={{ ...field.value, tokensToMint }} datatokenOptions={field.value.datatoken} - generateName={generateName} content={content.dynamic} /> ) diff --git a/src/components/pages/Publish/Preview.module.css b/src/components/pages/Publish/Preview.module.css index b95070c71..230e3b197 100644 --- a/src/components/pages/Publish/Preview.module.css +++ b/src/components/pages/Publish/Preview.module.css @@ -44,11 +44,3 @@ align-items: center; margin-bottom: calc(var(--spacer) / 2); } - -.price { - min-width: 0; -} - -.price:only-child { - margin-right: -100%; -} diff --git a/src/components/pages/Publish/Preview.tsx b/src/components/pages/Publish/Preview.tsx index 39e561444..f1a3fecf1 100644 --- a/src/components/pages/Publish/Preview.tsx +++ b/src/components/pages/Publish/Preview.tsx @@ -7,8 +7,6 @@ import styles from './Preview.module.css' import File from '../../atoms/File' import { MetadataPublishForm } from '../../../@types/MetaData' import Button from '../../atoms/Button' -import Conversion from '../../atoms/Price/Conversion' -import PriceUnit from '../../atoms/Price/PriceUnit' export default function Preview({ values @@ -60,25 +58,6 @@ export default function Preview({ small /> )} - - {values.price && ( -
- - {' '} - = - - - } - /> -
- )} {typeof values.links !== 'string' && values.links?.length && ( @@ -107,7 +86,7 @@ export default function Preview({ key.includes('files') || key.includes('links') || key.includes('termsAndConditions') || - key.includes('price') || + key.includes('dataTokenOptions') || value === undefined || value === '' ) diff --git a/src/components/pages/Publish/Pricing.tsx b/src/components/pages/Publish/Pricing.tsx new file mode 100644 index 000000000..c932d6e82 --- /dev/null +++ b/src/components/pages/Publish/Pricing.tsx @@ -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 ( + { + await handlePricing(values.amount, resetForm) + setSubmitting(false) + }} + > + {({ + values, + touched, + setTouched, + isSubmitting, + setFieldValue, + submitForm, + handleChange + }) => ( + <> + + {({ + field, + form + }: { + field: FieldInputProps + form: any + }) => ( + + )} + + + )} + + ) +} diff --git a/src/components/pages/Publish/index.tsx b/src/components/pages/Publish/index.tsx index 6dfaae3cd..acde3b78c 100644 --- a/src/components/pages/Publish/index.tsx +++ b/src/components/pages/Publish/index.tsx @@ -36,18 +36,20 @@ export default function PublishPage({ resetForm: () => void ): Promise { const metadata = transformPublishFormToMetadata(values) - const { price } = values const serviceType = values.access === 'Download' ? 'access' : 'compute' try { - Logger.log('Publish with ', price, serviceType, price.datatoken) + Logger.log( + 'Publish with ', + metadata, + serviceType, + values.dataTokenOptions + ) const ddo = await publish( (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, - price.datatoken + values.dataTokenOptions ) // Publish failed diff --git a/src/components/pages/Publish/utils.ts b/src/components/pages/Publish/utils.ts index 10d377ef8..7814f2025 100644 --- a/src/components/pages/Publish/utils.ts +++ b/src/components/pages/Publish/utils.ts @@ -16,8 +16,7 @@ export function transformPublishFormToMetadata( tags, links, termsAndConditions, - files, - price + files } = data const metadata: MetadataMarket = { @@ -36,8 +35,7 @@ export function transformPublishFormToMetadata( copyrightHolder, tags: tags?.split(','), links: typeof links !== 'string' && links, - termsAndConditions, - priceType: price.type + termsAndConditions } } diff --git a/src/models/FormPricing.ts b/src/models/FormPricing.ts new file mode 100644 index 000000000..a01bd8e35 --- /dev/null +++ b/src/models/FormPricing.ts @@ -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({ + 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 = { + price: { + price: 1, + type: 'dynamic', + tokensToMint: 1, + weightOnDataToken: '9', // 90% on data token + swapFee: 0.1 // in % + } +} diff --git a/src/models/FormPublish.ts b/src/models/FormPublish.ts index 483f13e2b..6f85b8faf 100644 --- a/src/models/FormPublish.ts +++ b/src/models/FormPublish.ts @@ -6,26 +6,10 @@ export const validationSchema = Yup.object().shape({ // ---- required fields ---- name: 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({ - name: Yup.string(), - symbol: Yup.string() - }) - .nullable() + name: Yup.string(), + symbol: Yup.string() }) .required('Required'), files: Yup.array().required('Required').nullable(), @@ -45,12 +29,9 @@ export const validationSchema = Yup.object().shape({ export const initialValues: Partial = { name: '', author: '', - price: { - price: 1, - type: 'dynamic', - tokensToMint: 1, - weightOnDataToken: '9', // 90% on data token - swapFee: 0.1 // in % + dataTokenOptions: { + name: '', + symbol: '' }, files: '', description: '', From 8ce53714a4c2d9b7c23e7439069b6253d62a8b95 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 19 Oct 2020 15:04:57 +0200 Subject: [PATCH 06/32] more publish data flow changes --- src/components/pages/Publish/Feedback.tsx | 7 +-- src/components/pages/Publish/Pricing.tsx | 36 +++++++++------ src/components/pages/Publish/index.tsx | 8 ++-- src/models/FormPricing.ts | 53 ++++++++--------------- 4 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/components/pages/Publish/Feedback.tsx b/src/components/pages/Publish/Feedback.tsx index e4e0ea011..99537a276 100644 --- a/src/components/pages/Publish/Feedback.tsx +++ b/src/components/pages/Publish/Feedback.tsx @@ -4,17 +4,18 @@ import Loader from '../../atoms/Loader' import React, { ReactElement } from 'react' import styles from './Feedback.module.css' import SuccessConfetti from '../../atoms/SuccessConfetti' +import { DDO } from '@oceanprotocol/lib' export default function Feedback({ error, success, - did, + ddo, publishStepText, setError }: { error: string success: string - did: string + ddo: DDO publishStepText: string setError: (error: string) => void }): ReactElement { @@ -22,7 +23,7 @@ export default function Feedback({ + ) : isPricing ? ( + ) : success ? ( } /> ) : ( diff --git a/src/components/pages/Publish/Pricing.tsx b/src/components/pages/Publish/Pricing.tsx index de4509e99..087c0c20a 100644 --- a/src/components/pages/Publish/Pricing.tsx +++ b/src/components/pages/Publish/Pricing.tsx @@ -1,21 +1,20 @@ import React, { ReactElement } from 'react' import { Field, FieldInputProps, Formik } from 'formik' import Input from '../../atoms/Input' -import { initialValues, validationSchema } from 'models/FormPricing' +import { initialValues, validationSchema } from '../../../models/FormPricing' import { DDO } from '@oceanprotocol/lib' import { usePricing } from '@oceanprotocol/react' import { PriceOptionsMarket } from '../../../@types/MetaData' +import ddoFixture from '../../../../tests/unit/__fixtures__/ddo' export default function Pricing({ ddo }: { ddo: DDO }): ReactElement { - const { createPricing } = usePricing(ddo) + const { createPricing } = usePricing(ddoFixture) async function handleCreatePricing(values: Partial) { const priceOptions = { - price: values.price, - tokensToMint: values.tokensToMint, - type: values.type, - weightOnDataToken: values.weightOnDataToken, - swapFee: values.swapFee + ...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) } diff --git a/src/components/pages/Publish/index.tsx b/src/components/pages/Publish/index.tsx index 13edeb945..d744ce0c1 100644 --- a/src/components/pages/Publish/index.tsx +++ b/src/components/pages/Publish/index.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, useState } from 'react' import { Formik } from 'formik' -import { usePublish } from '@oceanprotocol/react' +import { usePublish, usePricing } from '@oceanprotocol/react' import styles from './index.module.css' import PublishForm from './PublishForm' import Web3Feedback from '../../molecules/Wallet/Feedback' @@ -27,6 +27,7 @@ export default function PublishPage({ const [success, setSuccess] = useState() const [error, setError] = useState() + const [isPricing, setIsPricing] = useState() const [ddo, setDdo] = useState() const hasFeedback = isLoading || error || success @@ -59,12 +60,16 @@ export default function PublishPage({ return } + if (!ddo) return + // Publish succeeded - if (ddo) { - setDdo(ddo) - setSuccess('🎉 Successfully published your data set. 🎉') - resetForm() - } + setDdo(ddo) + resetForm() + + // Create pricing + setIsPricing(true) + + // setSuccess('🎉 Successfully published your data set. 🎉') } catch (error) { setError(error.message) Logger.error(error.message) @@ -94,6 +99,7 @@ export default function PublishPage({ success={success} publishStepText={publishStepText} ddo={ddo} + isPricing={isPricing} setError={setError} /> ) : ( From 9ce1358fe12a209dedbd8cf6ea58d526c257ed42 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Tue, 20 Oct 2020 13:10:03 +0200 Subject: [PATCH 08/32] move around pricing view --- src/components/atoms/Alert.module.css | 6 +- src/components/atoms/Alert.tsx | 15 +++- .../organisms/AssetContent/Pricing.module.css | 3 + .../organisms/AssetContent/Pricing.tsx | 70 +++++++++++++++++++ .../organisms/AssetContent/index.module.css | 4 ++ .../organisms/AssetContent/index.tsx | 4 ++ src/components/pages/Publish/Feedback.tsx | 5 -- src/components/pages/Publish/Pricing.tsx | 54 -------------- src/components/pages/Publish/index.tsx | 9 +-- 9 files changed, 103 insertions(+), 67 deletions(-) create mode 100644 src/components/organisms/AssetContent/Pricing.module.css create mode 100644 src/components/organisms/AssetContent/Pricing.tsx delete mode 100644 src/components/pages/Publish/Pricing.tsx diff --git a/src/components/atoms/Alert.module.css b/src/components/atoms/Alert.module.css index c35f3b2de..f03ba01f0 100644 --- a/src/components/atoms/Alert.module.css +++ b/src/components/atoms/Alert.module.css @@ -30,6 +30,10 @@ font-size: var(--font-size-small); } +.action { + margin-top: calc(var(--spacer) / 2); +} + /* States */ .error { border-color: var(--rbrand-alert-ed); @@ -43,7 +47,7 @@ .info { border-color: var(--brand-alert-yellow); - color: var(--brand-alert-yellow); + color: #9f7e19; } .warning { diff --git a/src/components/atoms/Alert.tsx b/src/components/atoms/Alert.tsx index c1b7b0a14..1610d6001 100644 --- a/src/components/atoms/Alert.tsx +++ b/src/components/atoms/Alert.tsx @@ -1,19 +1,32 @@ import React, { ReactElement } from 'react' import styles from './Alert.module.css' +import Button from './Button' export default function Alert({ title, text, - state + state, + action }: { title?: string text: string state: 'error' | 'warning' | 'info' | 'success' + action?: { name: string; handleAction: () => void } }): ReactElement { return (
{title &&

{title}

}

{text}

+ {action && ( + + )}
) } diff --git a/src/components/organisms/AssetContent/Pricing.module.css b/src/components/organisms/AssetContent/Pricing.module.css new file mode 100644 index 000000000..fcfae9601 --- /dev/null +++ b/src/components/organisms/AssetContent/Pricing.module.css @@ -0,0 +1,3 @@ +.pricing { + margin-bottom: var(--spacer); +} diff --git a/src/components/organisms/AssetContent/Pricing.tsx b/src/components/organisms/AssetContent/Pricing.tsx new file mode 100644 index 000000000..c3232263e --- /dev/null +++ b/src/components/organisms/AssetContent/Pricing.tsx @@ -0,0 +1,70 @@ +import React, { ReactElement, useState } from 'react' +import { Field, FieldInputProps, Formik } from 'formik' +import Input from '../../atoms/Input' +import { initialValues, validationSchema } from '../../../models/FormPricing' +import { DDO } from '@oceanprotocol/lib' +import { usePricing } from '@oceanprotocol/react' +import { PriceOptionsMarket } from '../../../@types/MetaData' +import Alert from '../../atoms/Alert' +import styles from './Pricing.module.css' + +export default function Pricing({ ddo }: { ddo: DDO }): ReactElement { + const { createPricing } = usePricing(ddo) + const [showPricing, setShowPricing] = useState(false) + + async function handleCreatePricing(values: Partial) { + 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) + } + + return ( +
+ {showPricing ? ( + { + await handleCreatePricing(values) + setSubmitting(false) + }} + > + {() => ( + <> + + {({ + field, + form + }: { + field: FieldInputProps + form: any + }) => ( + + )} + + + )} + + ) : ( + setShowPricing(true) + }} + /> + )} +
+ ) +} diff --git a/src/components/organisms/AssetContent/index.module.css b/src/components/organisms/AssetContent/index.module.css index 874cc5810..9dd4a3115 100644 --- a/src/components/organisms/AssetContent/index.module.css +++ b/src/components/organisms/AssetContent/index.module.css @@ -1,3 +1,7 @@ +.pricing { + max-width: 40rem; +} + .grid { display: grid; gap: calc(var(--spacer) * 1.5); diff --git a/src/components/organisms/AssetContent/index.tsx b/src/components/organisms/AssetContent/index.tsx index dc4cfa35f..5ad04f7fc 100644 --- a/src/components/organisms/AssetContent/index.tsx +++ b/src/components/organisms/AssetContent/index.tsx @@ -9,6 +9,7 @@ import styles from './index.module.css' import AssetActions from '../AssetActions' import { DDO } from '@oceanprotocol/lib' import { useUserPreferences } from '../../../providers/UserPreferences' +import Pricing from './Pricing' export interface AssetContentProps { metadata: MetadataMarket @@ -26,6 +27,9 @@ export default function AssetContent({ return (
+ {/* TODO: check for ddo creator */} + +