mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Add warning to Add Liquidity screen (#177)
* refactor add form, warning component * dismissable alert * prototype default warning screen * add copy, finalize styling * remove dismiss x
This commit is contained in:
parent
290e43199c
commit
e96d782917
@ -34,7 +34,8 @@
|
|||||||
"titleIn": "You will receive",
|
"titleIn": "You will receive",
|
||||||
"titleOut": "You will earn"
|
"titleOut": "You will earn"
|
||||||
},
|
},
|
||||||
"action": "Approve & Supply"
|
"action": "Approve & Supply",
|
||||||
|
"warning": "Use at your own risk. Please familiarize yourself [with the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13) and the [Terms of Use](/terms)."
|
||||||
},
|
},
|
||||||
"remove": {
|
"remove": {
|
||||||
"title": "Remove Liquidity",
|
"title": "Remove Liquidity",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
padding-top: calc(var(--spacer) / 2);
|
padding-top: calc(var(--spacer) / 2);
|
||||||
padding-bottom: calc(var(--spacer) / 2);
|
padding-bottom: calc(var(--spacer) / 2);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert + .alert {
|
.alert + .alert {
|
||||||
@ -37,6 +38,24 @@
|
|||||||
margin-top: calc(var(--spacer) / 2);
|
margin-top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover,
|
||||||
|
.close:focus {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* States */
|
/* States */
|
||||||
.error {
|
.error {
|
||||||
border-color: var(--rbrand-alert-ed);
|
border-color: var(--rbrand-alert-ed);
|
||||||
|
@ -1,36 +1,55 @@
|
|||||||
import React, { ReactElement, FormEvent } from 'react'
|
import React, { ReactElement, FormEvent } from 'react'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
import styles from './Alert.module.css'
|
import styles from './Alert.module.css'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
import Markdown from './Markdown'
|
import Markdown from './Markdown'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function Alert({
|
export default function Alert({
|
||||||
title,
|
title,
|
||||||
text,
|
text,
|
||||||
state,
|
state,
|
||||||
action
|
action,
|
||||||
|
onDismiss,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
title?: string
|
title?: string
|
||||||
text: string
|
text: string
|
||||||
state: 'error' | 'warning' | 'info' | 'success'
|
state: 'error' | 'warning' | 'info' | 'success'
|
||||||
action?: {
|
action?: {
|
||||||
name: string
|
name: string
|
||||||
|
style?: 'text' | 'primary' | 'ghost'
|
||||||
handleAction: (e: FormEvent<HTMLButtonElement>) => void
|
handleAction: (e: FormEvent<HTMLButtonElement>) => void
|
||||||
}
|
}
|
||||||
|
onDismiss?: () => void
|
||||||
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const styleClasses = cx({
|
||||||
|
alert: true,
|
||||||
|
[state]: state,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.alert} ${styles[state]}`}>
|
<div className={styleClasses}>
|
||||||
{title && <h3 className={styles.title}>{title}</h3>}
|
{title && <h3 className={styles.title}>{title}</h3>}
|
||||||
<Markdown className={styles.text} text={text} />
|
<Markdown className={styles.text} text={text} />
|
||||||
{action && (
|
{action && (
|
||||||
<Button
|
<Button
|
||||||
className={styles.action}
|
className={styles.action}
|
||||||
size="small"
|
size="small"
|
||||||
style="primary"
|
style={action.style || 'primary'}
|
||||||
onClick={action.handleAction}
|
onClick={action.handleAction}
|
||||||
>
|
>
|
||||||
{action.name}
|
{action.name}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{onDismiss && (
|
||||||
|
<button className={styles.close} onClick={onDismiss}>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,3 @@
|
|||||||
.addInput {
|
|
||||||
margin: 0 auto calc(var(--spacer) / 1.5) auto;
|
|
||||||
background: var(--brand-grey-dimmed);
|
|
||||||
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;
|
|
||||||
margin-right: -2rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addInput input {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addInput div[class*='field'] {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonMax {
|
.buttonMax {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
@ -41,22 +21,3 @@
|
|||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
transform-origin: right center;
|
transform-origin: right center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.output {
|
|
||||||
display: grid;
|
|
||||||
gap: var(--spacer);
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.output p {
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
margin-bottom: calc(var(--spacer) / 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.output [class*='token'] {
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.output [class*='token'] > figure {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import PriceUnit from '../../../../atoms/Price/PriceUnit'
|
import PriceUnit from '../../../../atoms/Price/PriceUnit'
|
||||||
import React, { ChangeEvent, ReactElement, useEffect, useState } from 'react'
|
import React, { ChangeEvent, ReactElement, useEffect } from 'react'
|
||||||
import styles from './FormAdd.module.css'
|
import styles from './FormAdd.module.css'
|
||||||
import Input from '../../../../atoms/Input'
|
import Input from '../../../../atoms/Input'
|
||||||
import {
|
import {
|
||||||
@ -9,14 +9,12 @@ import {
|
|||||||
useFormikContext
|
useFormikContext
|
||||||
} from 'formik'
|
} from 'formik'
|
||||||
import Button from '../../../../atoms/Button'
|
import Button from '../../../../atoms/Button'
|
||||||
import Token from '../Token'
|
|
||||||
import CoinSelect from '../CoinSelect'
|
import CoinSelect from '../CoinSelect'
|
||||||
import { FormAddLiquidity } from '.'
|
import { FormAddLiquidity } from '.'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import { Balance } from '..'
|
import { Balance } from '..'
|
||||||
|
|
||||||
export default function FormAdd({
|
export default function FormAdd({
|
||||||
content,
|
|
||||||
coin,
|
coin,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
dtSymbol,
|
dtSymbol,
|
||||||
@ -24,10 +22,10 @@ export default function FormAdd({
|
|||||||
setCoin,
|
setCoin,
|
||||||
totalPoolTokens,
|
totalPoolTokens,
|
||||||
totalBalance,
|
totalBalance,
|
||||||
swapFee,
|
poolAddress,
|
||||||
poolAddress
|
setNewPoolTokens,
|
||||||
|
setNewPoolShare
|
||||||
}: {
|
}: {
|
||||||
content: any
|
|
||||||
coin: string
|
coin: string
|
||||||
dtBalance: string
|
dtBalance: string
|
||||||
dtSymbol: string
|
dtSymbol: string
|
||||||
@ -35,8 +33,9 @@ export default function FormAdd({
|
|||||||
setCoin: (value: string) => void
|
setCoin: (value: string) => void
|
||||||
totalPoolTokens: string
|
totalPoolTokens: string
|
||||||
totalBalance: Balance
|
totalBalance: Balance
|
||||||
swapFee: string
|
|
||||||
poolAddress: string
|
poolAddress: string
|
||||||
|
setNewPoolTokens: (value: string) => void
|
||||||
|
setNewPoolShare: (value: string) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, balance } = useOcean()
|
const { ocean, balance } = useOcean()
|
||||||
|
|
||||||
@ -49,9 +48,6 @@ export default function FormAdd({
|
|||||||
values
|
values
|
||||||
}: FormikContextType<FormAddLiquidity> = useFormikContext()
|
}: FormikContextType<FormAddLiquidity> = useFormikContext()
|
||||||
|
|
||||||
const [newPoolTokens, setNewPoolTokens] = useState('0')
|
|
||||||
const [newPoolShare, setNewPoolShare] = useState('0')
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function calculatePoolShares() {
|
async function calculatePoolShares() {
|
||||||
if (!values.amount) {
|
if (!values.amount) {
|
||||||
@ -80,71 +76,58 @@ export default function FormAdd({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.addInput}>
|
<div className={styles.userLiquidity}>
|
||||||
<div className={styles.userLiquidity}>
|
<div>
|
||||||
<div>
|
<span>Available:</span>
|
||||||
<span>Available:</span>
|
{coin === 'OCEAN' ? (
|
||||||
{coin === 'OCEAN' ? (
|
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
||||||
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
) : (
|
||||||
) : (
|
<PriceUnit price={dtBalance} symbol={dtSymbol} small />
|
||||||
<PriceUnit price={dtBalance} symbol={dtSymbol} small />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Maximum:</span>
|
|
||||||
<PriceUnit price={amountMax} symbol={coin} small />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Field name="amount">
|
|
||||||
{({
|
|
||||||
field,
|
|
||||||
form
|
|
||||||
}: {
|
|
||||||
field: FieldInputProps<FormAddLiquidity>
|
|
||||||
form: any
|
|
||||||
}) => (
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
name="amount"
|
|
||||||
max={amountMax}
|
|
||||||
value={`${values.amount}`}
|
|
||||||
prefix={<CoinSelect dtSymbol={dtSymbol} setCoin={setCoin} />}
|
|
||||||
placeholder="0"
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
// Workaround so validation kicks in on first touch
|
|
||||||
!touched?.amount && setTouched({ amount: true })
|
|
||||||
handleChange(e)
|
|
||||||
}}
|
|
||||||
disabled={!ocean}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Field>
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Maximum:</span>
|
||||||
|
<PriceUnit price={amountMax} symbol={coin} small />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{(Number(balance.ocean) || dtBalance) > (values.amount || 0) && (
|
<Field name="amount">
|
||||||
<Button
|
{({
|
||||||
className={styles.buttonMax}
|
field,
|
||||||
style="text"
|
form
|
||||||
size="small"
|
}: {
|
||||||
onClick={() => setFieldValue('amount', amountMax)}
|
field: FieldInputProps<FormAddLiquidity>
|
||||||
>
|
form: any
|
||||||
Use Max
|
}) => (
|
||||||
</Button>
|
<Input
|
||||||
|
type="number"
|
||||||
|
name="amount"
|
||||||
|
max={amountMax}
|
||||||
|
value={`${values.amount}`}
|
||||||
|
prefix={<CoinSelect dtSymbol={dtSymbol} setCoin={setCoin} />}
|
||||||
|
placeholder="0"
|
||||||
|
field={field}
|
||||||
|
form={form}
|
||||||
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// Workaround so validation kicks in on first touch
|
||||||
|
!touched?.amount && setTouched({ amount: true })
|
||||||
|
handleChange(e)
|
||||||
|
}}
|
||||||
|
disabled={!ocean}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Field>
|
||||||
|
|
||||||
<div className={styles.output}>
|
{(Number(balance.ocean) || dtBalance) > (values.amount || 0) && (
|
||||||
<div>
|
<Button
|
||||||
<p>{content.output.titleIn}</p>
|
className={styles.buttonMax}
|
||||||
<Token symbol="pool shares" balance={newPoolTokens} />
|
style="text"
|
||||||
<Token symbol="% of pool" balance={newPoolShare} />
|
size="small"
|
||||||
</div>
|
onClick={() => setFieldValue('amount', amountMax)}
|
||||||
<div>
|
>
|
||||||
<p>{content.output.titleOut}</p>
|
Use Max
|
||||||
<Token symbol="% swap fee" balance={swapFee} />
|
</Button>
|
||||||
</div>
|
)}
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
.addInput {
|
||||||
|
margin: 0 auto calc(var(--spacer) / 1.5) auto;
|
||||||
|
background: var(--brand-grey-dimmed);
|
||||||
|
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;
|
||||||
|
margin-right: -2rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addInput input {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addInput div[class*='field'] {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output p {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.output [class*='token'] {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output [class*='token'] > figure {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
margin-left: -3rem;
|
||||||
|
margin-right: -3rem;
|
||||||
|
}
|
@ -9,6 +9,8 @@ import * as Yup from 'yup'
|
|||||||
import { Formik } from 'formik'
|
import { Formik } from 'formik'
|
||||||
import FormAdd from './FormAdd'
|
import FormAdd from './FormAdd'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
import Token from '../Token'
|
||||||
|
import Alert from '../../../../atoms/Alert'
|
||||||
|
|
||||||
const contentQuery = graphql`
|
const contentQuery = graphql`
|
||||||
query PoolAddQuery {
|
query PoolAddQuery {
|
||||||
@ -24,6 +26,7 @@ const contentQuery = graphql`
|
|||||||
titleOut
|
titleOut
|
||||||
}
|
}
|
||||||
action
|
action
|
||||||
|
warning
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,6 +71,9 @@ export default function Add({
|
|||||||
const [coin, setCoin] = useState('OCEAN')
|
const [coin, setCoin] = useState('OCEAN')
|
||||||
const [dtBalance, setDtBalance] = useState<string>()
|
const [dtBalance, setDtBalance] = useState<string>()
|
||||||
const [amountMax, setAmountMax] = useState<string>()
|
const [amountMax, setAmountMax] = useState<string>()
|
||||||
|
const [newPoolTokens, setNewPoolTokens] = useState('0')
|
||||||
|
const [newPoolShare, setNewPoolShare] = useState('0')
|
||||||
|
const [isWarningAccepted, setIsWarningAccepted] = useState(false)
|
||||||
|
|
||||||
// Live validation rules
|
// Live validation rules
|
||||||
// https://github.com/jquense/yup#number
|
// https://github.com/jquense/yup#number
|
||||||
@ -150,18 +156,45 @@ export default function Add({
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, submitForm }) => (
|
{({ isSubmitting, submitForm }) => (
|
||||||
<>
|
<>
|
||||||
<FormAdd
|
<div className={styles.addInput}>
|
||||||
coin={coin}
|
{isWarningAccepted ? (
|
||||||
content={content}
|
<FormAdd
|
||||||
dtBalance={dtBalance}
|
coin={coin}
|
||||||
dtSymbol={dtSymbol}
|
dtBalance={dtBalance}
|
||||||
amountMax={amountMax}
|
dtSymbol={dtSymbol}
|
||||||
setCoin={setCoin}
|
amountMax={amountMax}
|
||||||
totalPoolTokens={totalPoolTokens}
|
setCoin={setCoin}
|
||||||
totalBalance={totalBalance}
|
totalPoolTokens={totalPoolTokens}
|
||||||
swapFee={swapFee}
|
totalBalance={totalBalance}
|
||||||
poolAddress={poolAddress}
|
poolAddress={poolAddress}
|
||||||
/>
|
setNewPoolTokens={setNewPoolTokens}
|
||||||
|
setNewPoolShare={setNewPoolShare}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Alert
|
||||||
|
className={styles.warning}
|
||||||
|
text={content.warning}
|
||||||
|
state="info"
|
||||||
|
action={{
|
||||||
|
name: 'I understand',
|
||||||
|
style: 'text',
|
||||||
|
handleAction: () => setIsWarningAccepted(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.output}>
|
||||||
|
<div>
|
||||||
|
<p>{content.output.titleIn}</p>
|
||||||
|
<Token symbol="pool shares" balance={newPoolTokens} />
|
||||||
|
<Token symbol="% of pool" balance={newPoolShare} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>{content.output.titleOut}</p>
|
||||||
|
<Token symbol="% swap fee" balance={swapFee} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Actions
|
<Actions
|
||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
|
Loading…
Reference in New Issue
Block a user