From 8bec69986c9379ddaf39ee6ab25038e4e10be9ee Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 15 Oct 2020 12:48:01 +0200 Subject: [PATCH 01/12] add max amount checks --- .../AssetActions/Pool/Add.module.css | 7 +++- .../organisms/AssetActions/Pool/Add.tsx | 42 +++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/components/organisms/AssetActions/Pool/Add.module.css b/src/components/organisms/AssetActions/Pool/Add.module.css index 1fc546251..3aab2d915 100644 --- a/src/components/organisms/AssetActions/Pool/Add.module.css +++ b/src/components/organisms/AssetActions/Pool/Add.module.css @@ -21,15 +21,18 @@ right: calc(var(--spacer) * 3); } -.userLiquidity { +.userLiquidity > div { display: flex; justify-content: space-between; align-items: center; font-size: var(--font-size-mini); - margin-bottom: calc(var(--spacer) / 4); color: var(--color-secondary); } +.userLiquidity > div:last-child { + margin-bottom: calc(var(--spacer) / 4); +} + .userLiquidity span + div { transform: scale(0.8); transform-origin: right center; diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index 270b97de5..e49ea0327 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -12,6 +12,7 @@ import Actions from './Actions' import Tooltip from '../../../atoms/Tooltip' import { ReactComponent as Caret } from '../../../../images/caret.svg' import { graphql, useStaticQuery } from 'gatsby' +import FormHelp from '../../../atoms/Input/Help' const contentQuery = graphql` query PoolAddQuery { @@ -62,6 +63,7 @@ export default function Add({ const [isLoading, setIsLoading] = useState() const [coin, setCoin] = useState('OCEAN') const [dtBalance, setDtBalance] = useState() + const [amountMax, setAmountMax] = useState() const newPoolTokens = totalBalance && @@ -71,15 +73,30 @@ export default function Add({ totalBalance && ((Number(newPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2) + // Get datatoken balance when datatoken selected useEffect(() => { - if (!ocean) return + if (!ocean || coin === 'OCEAN') return async function getDtBalance() { const dtBalance = await ocean.datatokens.balance(dtAddress, accountId) setDtBalance(dtBalance) } getDtBalance() - }, [ocean, accountId, dtAddress]) + }, [ocean, accountId, dtAddress, coin]) + + // Get maximum amount for either OCEAN or datatoken + useEffect(() => { + if (!ocean) return + + async function getMaximum() { + const amountMax = + coin === 'OCEAN' + ? await ocean.pool.getOceanMaxAddLiquidity(poolAddress) + : await ocean.pool.getDTMaxAddLiquidity(poolAddress) + setAmountMax(amountMax) + } + getMaximum() + }, [ocean, poolAddress, coin]) async function handleAddLiquidity() { setIsLoading(true) @@ -104,7 +121,7 @@ export default function Add({ } function handleMax() { - setAmount(coin === 'OCEAN' ? balance.ocean : dtBalance) + setAmount(amountMax) } // TODO: this is only a prototype and is an accessibility nightmare. @@ -124,18 +141,25 @@ export default function Add({
- Available: - {coin === 'OCEAN' ? ( - - ) : ( - - )} +
+ Available: + {coin === 'OCEAN' ? ( + + ) : ( + + )} +
+
+ Maximum: + +
} From 5a7955f7aab938894d8504cec06eb26363018d10 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 15 Oct 2020 13:47:03 +0200 Subject: [PATCH 02/12] refactor to use Formik for better live validation --- .../organisms/AssetActions/Pool/Add.tsx | 197 ++++++++++-------- 1 file changed, 114 insertions(+), 83 deletions(-) diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index e49ea0327..22ad1f065 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -12,7 +12,9 @@ import Actions from './Actions' import Tooltip from '../../../atoms/Tooltip' import { ReactComponent as Caret } from '../../../../images/caret.svg' import { graphql, useStaticQuery } from 'gatsby' -import FormHelp from '../../../atoms/Input/Help' +import * as Yup from 'yup' +import { Field, Formik } from 'formik' +import Input from '../../../atoms/Input' const contentQuery = graphql` query PoolAddQuery { @@ -37,6 +39,10 @@ const contentQuery = graphql` } ` +const initialValues: Partial<{ amount: number }> = { + amount: 1 +} + export default function Add({ setShowAdd, poolAddress, @@ -58,21 +64,12 @@ export default function Add({ const content = data.content.edges[0].node.childContentJson.pool.add const { ocean, accountId, balance } = useOcean() - const [amount, setAmount] = useState('') const [txId, setTxId] = useState('') const [isLoading, setIsLoading] = useState() const [coin, setCoin] = useState('OCEAN') const [dtBalance, setDtBalance] = useState() const [amountMax, setAmountMax] = useState() - const newPoolTokens = - totalBalance && - ((Number(amount) / Number(totalBalance.ocean)) * 100).toFixed(2) - - const newPoolShare = - totalBalance && - ((Number(newPoolTokens) / Number(totalPoolTokens)) * 100).toFixed(2) - // Get datatoken balance when datatoken selected useEffect(() => { if (!ocean || coin === 'OCEAN') return @@ -98,16 +95,21 @@ export default function Add({ getMaximum() }, [ocean, poolAddress, coin]) - async function handleAddLiquidity() { + async function handleAddLiquidity(amount: number, resetForm: () => void) { setIsLoading(true) try { const result = coin === 'OCEAN' - ? await ocean.pool.addOceanLiquidity(accountId, poolAddress, amount) - : await ocean.pool.addDTLiquidity(accountId, poolAddress, amount) + ? await ocean.pool.addOceanLiquidity( + accountId, + poolAddress, + `${amount}` + ) + : await ocean.pool.addDTLiquidity(accountId, poolAddress, `${amount}`) setTxId(result?.transactionHash) + resetForm() } catch (error) { console.error(error.message) toast.error(error.message) @@ -116,13 +118,15 @@ export default function Add({ } } - function handleAmountChange(e: ChangeEvent) { - setAmount(e.target.value) - } - - function handleMax() { - setAmount(amountMax) - } + const validationSchema = Yup.object().shape<{ amount: number }>({ + amount: Yup.number() + .min(1, 'Must be more or equal to 1') + .max( + Number(amountMax), + `Must be less or equal to ${Number(amountMax).toFixed(2)}` + ) + .required('Required') + }) // TODO: this is only a prototype and is an accessibility nightmare. // Needs to be refactored to either use custom select element instead of tippy.js, @@ -139,73 +143,100 @@ export default function Add({ <>
setShowAdd(false)} /> -
-
-
- Available: - {coin === 'OCEAN' ? ( - - ) : ( - - )} -
-
- Maximum: - -
-
+ { + console.log('Hello') + // kick off + await handleAddLiquidity(values.amount, resetForm) + setSubmitting(false) + }} + > + {({ values, setFieldValue, submitForm }) => { + const newPoolTokens = + totalBalance && + ((values.amount / Number(totalBalance.ocean)) * 100).toFixed(2) - } - trigger="click focus" - className={styles.coinswitch} - placement="bottom" - > - {coin} -
+ return ( + <> +
+
+
+ Available: + {coin === 'OCEAN' ? ( + + ) : ( + + )} +
+
+ Maximum: + +
+
-
-
-

{content.output.titleIn}

- - -
-
-

{content.output.titleOut}

- -
-
+ + {({ field }: { field: any }) => ( + } + trigger="click focus" + className={styles.coinswitch} + placement="bottom" + > + {coin} + - + {(Number(balance.ocean) || dtBalance) > values.amount && ( + + )} +
+ +
+
+

{content.output.titleIn}

+ + +
+
+

{content.output.titleOut}

+ +
+
+ + + + ) + }} + ) } From d70ebba20d8b11a561873b5197785752525040b5 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 15 Oct 2020 14:09:02 +0200 Subject: [PATCH 03/12] error feedback --- src/components/atoms/Input/index.module.css | 13 +++++++++---- .../organisms/AssetActions/Pool/Add.module.css | 4 ++++ src/components/organisms/AssetActions/Pool/Add.tsx | 4 +--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/atoms/Input/index.module.css b/src/components/atoms/Input/index.module.css index 10cb7a97d..f7d68dd8a 100644 --- a/src/components/atoms/Input/index.module.css +++ b/src/components/atoms/Input/index.module.css @@ -13,12 +13,17 @@ } .error { - font-size: var(--font-size-small); - color: var(--brand-alert-red); + display: inline-block; + font-size: var(--font-size-mini); + font-weight: var(--font-weight-bold); + color: var(--brand-white); + background: var(--brand-alert-red); + border-radius: var(--border-radius); + padding: 0 0.4rem; position: absolute; - text-align: right; right: 0; - top: 0; + bottom: -0.75rem; + z-index: 1; } .hasError label { diff --git a/src/components/organisms/AssetActions/Pool/Add.module.css b/src/components/organisms/AssetActions/Pool/Add.module.css index 3aab2d915..5540ba65c 100644 --- a/src/components/organisms/AssetActions/Pool/Add.module.css +++ b/src/components/organisms/AssetActions/Pool/Add.module.css @@ -14,6 +14,10 @@ text-align: center; } +.addInput div[class*='field'] { + margin-bottom: 0; +} + .buttonMax { position: absolute; font-size: var(--font-size-mini); diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index 22ad1f065..3d3b39ad8 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -3,7 +3,6 @@ import styles from './Add.module.css' import { useOcean } from '@oceanprotocol/react' import Header from './Header' import { toast } from 'react-toastify' -import InputElement from '../../../atoms/Input/InputElement' import Button from '../../../atoms/Button' import Token from './Token' import { Balance } from './' @@ -40,7 +39,7 @@ const contentQuery = graphql` ` const initialValues: Partial<{ amount: number }> = { - amount: 1 + amount: undefined } export default function Add({ @@ -147,7 +146,6 @@ export default function Add({ initialValues={initialValues} validationSchema={validationSchema} onSubmit={async (values, { setSubmitting, resetForm }) => { - console.log('Hello') // kick off await handleAddLiquidity(values.amount, resetForm) setSubmitting(false) From 823513d4ee3ac673e2244455a44edf22252f9903 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 15 Oct 2020 14:16:38 +0200 Subject: [PATCH 04/12] error fixes --- src/components/atoms/Input/index.module.css | 10 +++++++--- src/components/atoms/Input/index.tsx | 2 +- src/components/organisms/AssetActions/Pool/Add.tsx | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/atoms/Input/index.module.css b/src/components/atoms/Input/index.module.css index f7d68dd8a..8f4536e50 100644 --- a/src/components/atoms/Input/index.module.css +++ b/src/components/atoms/Input/index.module.css @@ -15,14 +15,15 @@ .error { display: inline-block; font-size: var(--font-size-mini); + line-height: 1.2; font-weight: var(--font-weight-bold); color: var(--brand-white); background: var(--brand-alert-red); border-radius: var(--border-radius); - padding: 0 0.4rem; + padding: 0.2rem 0.4rem; position: absolute; right: 0; - bottom: -0.75rem; + top: 85%; z-index: 1; } @@ -31,7 +32,10 @@ } .hasError input, +.hasError input:focus, .hasError select, -.hasError textarea { +.hasError textarea, +.hasError [class*='prefix'], +.hasError [class*='postfix'] { border-color: var(--brand-alert-red); } diff --git a/src/components/atoms/Input/index.tsx b/src/components/atoms/Input/index.tsx index 68b49877f..875bdf65d 100644 --- a/src/components/atoms/Input/index.tsx +++ b/src/components/atoms/Input/index.tsx @@ -71,7 +71,7 @@ export default function Input(props: Partial): ReactElement { - {field && field.name !== 'price' && ( + {field && field.name !== 'price' && hasError && (
diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index 3d3b39ad8..d5e87cbfd 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -179,7 +179,7 @@ export default function Add({
- {({ field }: { field: any }) => ( + {({ field, form }: { field: any; form: any }) => ( )} From f60163b54d840ef1d3496c54842e773ee3af9dfa Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 15 Oct 2020 14:35:38 +0200 Subject: [PATCH 05/12] cleanup --- .../organisms/AssetActions/Pool/Add.tsx | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index d5e87cbfd..d9b04c998 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -12,7 +12,7 @@ import Tooltip from '../../../atoms/Tooltip' import { ReactComponent as Caret } from '../../../../images/caret.svg' import { graphql, useStaticQuery } from 'gatsby' import * as Yup from 'yup' -import { Field, Formik } from 'formik' +import { Field, FieldInputProps, Formik } from 'formik' import Input from '../../../atoms/Input' const contentQuery = graphql` @@ -38,7 +38,11 @@ const contentQuery = graphql` } ` -const initialValues: Partial<{ amount: number }> = { +interface FormAddLiquidity { + amount: number +} + +const initialValues: FormAddLiquidity = { amount: undefined } @@ -64,11 +68,22 @@ export default function Add({ const { ocean, accountId, balance } = useOcean() const [txId, setTxId] = useState('') - const [isLoading, setIsLoading] = useState() const [coin, setCoin] = useState('OCEAN') const [dtBalance, setDtBalance] = useState() const [amountMax, setAmountMax] = useState() + // Live validation rules + // https://github.com/jquense/yup#number + const validationSchema = Yup.object().shape({ + amount: Yup.number() + .min(1, 'Must be more or equal to 1') + .max( + Number(amountMax), + `Must be less or equal to ${Number(amountMax).toFixed(2)}` + ) + .required('Required') + }) + // Get datatoken balance when datatoken selected useEffect(() => { if (!ocean || coin === 'OCEAN') return @@ -94,9 +109,8 @@ export default function Add({ getMaximum() }, [ocean, poolAddress, coin]) + // Submit async function handleAddLiquidity(amount: number, resetForm: () => void) { - setIsLoading(true) - try { const result = coin === 'OCEAN' @@ -112,21 +126,9 @@ export default function Add({ } catch (error) { console.error(error.message) toast.error(error.message) - } finally { - setIsLoading(false) } } - const validationSchema = Yup.object().shape<{ amount: number }>({ - amount: Yup.number() - .min(1, 'Must be more or equal to 1') - .max( - Number(amountMax), - `Must be less or equal to ${Number(amountMax).toFixed(2)}` - ) - .required('Required') - }) - // TODO: this is only a prototype and is an accessibility nightmare. // Needs to be refactored to either use custom select element instead of tippy.js, // or use )} - + ) } From 997db111bdb037bf1deb78e64eeac00c372993e3 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Fri, 16 Oct 2020 09:00:16 +0200 Subject: [PATCH 07/12] new coin selection --- .../atoms/Input/InputElement.module.css | 5 ++-- .../AssetActions/Pool/Add.module.css | 24 +++-------------- .../organisms/AssetActions/Pool/Add.tsx | 26 +++---------------- .../AssetActions/Pool/CoinSelect.module.css | 25 ++++++++++++++++++ .../AssetActions/Pool/CoinSelect.tsx | 24 +++++++++++++++++ 5 files changed, 58 insertions(+), 46 deletions(-) create mode 100644 src/components/organisms/AssetActions/Pool/CoinSelect.module.css create mode 100644 src/components/organisms/AssetActions/Pool/CoinSelect.tsx diff --git a/src/components/atoms/Input/InputElement.module.css b/src/components/atoms/Input/InputElement.module.css index 8d98cb4bd..f51f1b3f1 100644 --- a/src/components/atoms/Input/InputElement.module.css +++ b/src/components/atoms/Input/InputElement.module.css @@ -11,7 +11,7 @@ margin: 0; border-radius: var(--border-radius); transition: 0.2s ease-out; - min-height: 43px; + height: 43px; min-width: 0; appearance: none; display: block; @@ -179,7 +179,7 @@ .prefix, .postfix { border: 1px solid var(--brand-grey-lighter); - min-height: 43px; + height: 43px; display: flex; align-items: center; padding-left: calc(var(--spacer) / 4); @@ -188,6 +188,7 @@ font-size: var(--font-size-small); transition: border 0.2s ease-out; white-space: nowrap; + position: relative; } .prefix { diff --git a/src/components/organisms/AssetActions/Pool/Add.module.css b/src/components/organisms/AssetActions/Pool/Add.module.css index 5540ba65c..655e4bda7 100644 --- a/src/components/organisms/AssetActions/Pool/Add.module.css +++ b/src/components/organisms/AssetActions/Pool/Add.module.css @@ -1,8 +1,8 @@ .addInput { margin: 0 auto calc(var(--spacer) / 1.5) auto; background: var(--brand-grey-dimmed); - padding: var(--spacer) calc(var(--spacer) * 3) calc(var(--spacer) * 1.2) - calc(var(--spacer) * 3); + padding: var(--spacer) calc(var(--spacer) * 2.5) calc(var(--spacer) * 1.2) + calc(var(--spacer) * 2.5); border-bottom: 1px solid var(--brand-grey-lighter); margin-top: -2rem; margin-left: -2rem; @@ -22,7 +22,7 @@ position: absolute; font-size: var(--font-size-mini); bottom: calc(var(--spacer) / 2); - right: calc(var(--spacer) * 3); + right: calc(var(--spacer) * 2.5); } .userLiquidity > div { @@ -42,24 +42,6 @@ transform-origin: right center; } -.coinswitch, -.coinPopover li { - cursor: pointer; -} - -.coinswitch svg { - width: 0.6em; - height: 0.6em; - display: inline-block; - fill: currentColor; - margin-right: 0.5rem; - margin-left: 0.25rem; -} - -.coinPopover li { - padding: calc(var(--spacer) / 4) calc(var(--spacer) / 2); -} - .output { display: grid; gap: var(--spacer); diff --git a/src/components/organisms/AssetActions/Pool/Add.tsx b/src/components/organisms/AssetActions/Pool/Add.tsx index 8ec83092d..4726e0d05 100644 --- a/src/components/organisms/AssetActions/Pool/Add.tsx +++ b/src/components/organisms/AssetActions/Pool/Add.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useState, useEffect, ChangeEvent } from 'react' +import React, { ReactElement, useState, useEffect } from 'react' import styles from './Add.module.css' import { useOcean } from '@oceanprotocol/react' import Header from './Header' @@ -8,12 +8,11 @@ import Token from './Token' import { Balance } from './' import PriceUnit from '../../../atoms/Price/PriceUnit' import Actions from './Actions' -import Tooltip from '../../../atoms/Tooltip' -import { ReactComponent as Caret } from '../../../../images/caret.svg' import { graphql, useStaticQuery } from 'gatsby' import * as Yup from 'yup' import { Field, Formik } from 'formik' import Input from '../../../atoms/Input' +import CoinSelect from './CoinSelect' const contentQuery = graphql` query PoolAddQuery { @@ -138,17 +137,6 @@ export default function Add({ } } - // TODO: this is only a prototype and is an accessibility nightmare. - // Needs to be refactored to either use custom select element instead of tippy.js, - // or use