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",
|
||||
"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": {
|
||||
"title": "Remove Liquidity",
|
||||
|
@ -8,6 +8,7 @@
|
||||
border-bottom-left-radius: 0;
|
||||
padding-top: calc(var(--spacer) / 2);
|
||||
padding-bottom: calc(var(--spacer) / 2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alert + .alert {
|
||||
@ -37,6 +38,24 @@
|
||||
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 */
|
||||
.error {
|
||||
border-color: var(--rbrand-alert-ed);
|
||||
|
@ -1,36 +1,55 @@
|
||||
import React, { ReactElement, FormEvent } from 'react'
|
||||
import classNames from 'classnames/bind'
|
||||
import styles from './Alert.module.css'
|
||||
import Button from './Button'
|
||||
import Markdown from './Markdown'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
export default function Alert({
|
||||
title,
|
||||
text,
|
||||
state,
|
||||
action
|
||||
action,
|
||||
onDismiss,
|
||||
className
|
||||
}: {
|
||||
title?: string
|
||||
text: string
|
||||
state: 'error' | 'warning' | 'info' | 'success'
|
||||
action?: {
|
||||
name: string
|
||||
style?: 'text' | 'primary' | 'ghost'
|
||||
handleAction: (e: FormEvent<HTMLButtonElement>) => void
|
||||
}
|
||||
onDismiss?: () => void
|
||||
className?: string
|
||||
}): ReactElement {
|
||||
const styleClasses = cx({
|
||||
alert: true,
|
||||
[state]: state,
|
||||
[className]: className
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={`${styles.alert} ${styles[state]}`}>
|
||||
<div className={styleClasses}>
|
||||
{title && <h3 className={styles.title}>{title}</h3>}
|
||||
<Markdown className={styles.text} text={text} />
|
||||
{action && (
|
||||
<Button
|
||||
className={styles.action}
|
||||
size="small"
|
||||
style="primary"
|
||||
style={action.style || 'primary'}
|
||||
onClick={action.handleAction}
|
||||
>
|
||||
{action.name}
|
||||
</Button>
|
||||
)}
|
||||
{onDismiss && (
|
||||
<button className={styles.close} onClick={onDismiss}>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</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 {
|
||||
position: absolute;
|
||||
font-size: var(--font-size-mini);
|
||||
@ -41,22 +21,3 @@
|
||||
transform: scale(0.8);
|
||||
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 React, { ChangeEvent, ReactElement, useEffect, useState } from 'react'
|
||||
import React, { ChangeEvent, ReactElement, useEffect } from 'react'
|
||||
import styles from './FormAdd.module.css'
|
||||
import Input from '../../../../atoms/Input'
|
||||
import {
|
||||
@ -9,14 +9,12 @@ import {
|
||||
useFormikContext
|
||||
} from 'formik'
|
||||
import Button from '../../../../atoms/Button'
|
||||
import Token from '../Token'
|
||||
import CoinSelect from '../CoinSelect'
|
||||
import { FormAddLiquidity } from '.'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
import { Balance } from '..'
|
||||
|
||||
export default function FormAdd({
|
||||
content,
|
||||
coin,
|
||||
dtBalance,
|
||||
dtSymbol,
|
||||
@ -24,10 +22,10 @@ export default function FormAdd({
|
||||
setCoin,
|
||||
totalPoolTokens,
|
||||
totalBalance,
|
||||
swapFee,
|
||||
poolAddress
|
||||
poolAddress,
|
||||
setNewPoolTokens,
|
||||
setNewPoolShare
|
||||
}: {
|
||||
content: any
|
||||
coin: string
|
||||
dtBalance: string
|
||||
dtSymbol: string
|
||||
@ -35,8 +33,9 @@ export default function FormAdd({
|
||||
setCoin: (value: string) => void
|
||||
totalPoolTokens: string
|
||||
totalBalance: Balance
|
||||
swapFee: string
|
||||
poolAddress: string
|
||||
setNewPoolTokens: (value: string) => void
|
||||
setNewPoolShare: (value: string) => void
|
||||
}): ReactElement {
|
||||
const { ocean, balance } = useOcean()
|
||||
|
||||
@ -49,9 +48,6 @@ export default function FormAdd({
|
||||
values
|
||||
}: FormikContextType<FormAddLiquidity> = useFormikContext()
|
||||
|
||||
const [newPoolTokens, setNewPoolTokens] = useState('0')
|
||||
const [newPoolShare, setNewPoolShare] = useState('0')
|
||||
|
||||
useEffect(() => {
|
||||
async function calculatePoolShares() {
|
||||
if (!values.amount) {
|
||||
@ -80,71 +76,58 @@ export default function FormAdd({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.addInput}>
|
||||
<div className={styles.userLiquidity}>
|
||||
<div>
|
||||
<span>Available:</span>
|
||||
{coin === 'OCEAN' ? (
|
||||
<PriceUnit price={balance.ocean} symbol="OCEAN" 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}
|
||||
/>
|
||||
<div className={styles.userLiquidity}>
|
||||
<div>
|
||||
<span>Available:</span>
|
||||
{coin === 'OCEAN' ? (
|
||||
<PriceUnit price={balance.ocean} symbol="OCEAN" small />
|
||||
) : (
|
||||
<PriceUnit price={dtBalance} symbol={dtSymbol} small />
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
<div>
|
||||
<span>Maximum:</span>
|
||||
<PriceUnit price={amountMax} symbol={coin} small />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(Number(balance.ocean) || dtBalance) > (values.amount || 0) && (
|
||||
<Button
|
||||
className={styles.buttonMax}
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={() => setFieldValue('amount', amountMax)}
|
||||
>
|
||||
Use Max
|
||||
</Button>
|
||||
<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}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
<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>
|
||||
{(Number(balance.ocean) || dtBalance) > (values.amount || 0) && (
|
||||
<Button
|
||||
className={styles.buttonMax}
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={() => setFieldValue('amount', amountMax)}
|
||||
>
|
||||
Use Max
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -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 FormAdd from './FormAdd'
|
||||
import styles from './index.module.css'
|
||||
import Token from '../Token'
|
||||
import Alert from '../../../../atoms/Alert'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query PoolAddQuery {
|
||||
@ -24,6 +26,7 @@ const contentQuery = graphql`
|
||||
titleOut
|
||||
}
|
||||
action
|
||||
warning
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,6 +71,9 @@ export default function Add({
|
||||
const [coin, setCoin] = useState('OCEAN')
|
||||
const [dtBalance, setDtBalance] = 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
|
||||
// https://github.com/jquense/yup#number
|
||||
@ -150,18 +156,45 @@ export default function Add({
|
||||
>
|
||||
{({ isSubmitting, submitForm }) => (
|
||||
<>
|
||||
<FormAdd
|
||||
coin={coin}
|
||||
content={content}
|
||||
dtBalance={dtBalance}
|
||||
dtSymbol={dtSymbol}
|
||||
amountMax={amountMax}
|
||||
setCoin={setCoin}
|
||||
totalPoolTokens={totalPoolTokens}
|
||||
totalBalance={totalBalance}
|
||||
swapFee={swapFee}
|
||||
poolAddress={poolAddress}
|
||||
/>
|
||||
<div className={styles.addInput}>
|
||||
{isWarningAccepted ? (
|
||||
<FormAdd
|
||||
coin={coin}
|
||||
dtBalance={dtBalance}
|
||||
dtSymbol={dtSymbol}
|
||||
amountMax={amountMax}
|
||||
setCoin={setCoin}
|
||||
totalPoolTokens={totalPoolTokens}
|
||||
totalBalance={totalBalance}
|
||||
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
|
||||
isLoading={isSubmitting}
|
||||
|
Loading…
Reference in New Issue
Block a user