1
0
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:
Matthias Kretschmann 2020-10-30 13:31:27 +01:00 committed by GitHub
parent 290e43199c
commit e96d782917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 185 additions and 126 deletions

View File

@ -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",

View File

@ -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);

View File

@ -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}>
&times;
</button>
)}
</div>
)
}

View File

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

View File

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

View File

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

View File

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