diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx index 9c625041..aa76e621 100644 --- a/src/components/Accordion.tsx +++ b/src/components/Accordion.tsx @@ -1,46 +1,30 @@ -import classNames from 'classnames' - import Card from 'components/Card' -import { Plus, Subtract } from 'components/Icons' -import Text from 'components/Text' + +import AccordionContent, { Item } from './AccordionContent' interface Props { items: Item[] -} - -interface Item { - title: string - content: React.ReactNode - open?: boolean + allowMultipleOpen?: boolean } export default function Accordion(props: Props) { + if (props.allowMultipleOpen) { + return ( + + {props.items.map((item, index) => ( + + ))} + + ) + } + return ( - - {props.items.map((item) => ( -
- - {item.title} -
- -
-
- -
-
-
{item.content}
-
+
+ {props.items.map((item, index) => ( + + + ))} - +
) } diff --git a/src/components/AccordionContent.tsx b/src/components/AccordionContent.tsx new file mode 100644 index 00000000..c54d432d --- /dev/null +++ b/src/components/AccordionContent.tsx @@ -0,0 +1,40 @@ +import classNames from 'classnames' + +import { ChevronDown, ChevronRight } from 'components/Icons' +import Text from 'components/Text' + +interface Props { + item: Item + index: number +} + +export interface Item { + title: string + renderContent: () => React.ReactNode + isOpen?: boolean + toggleOpen: (index: number) => void +} + +export default function AccordionContent(props: Props) { + return ( +
+
props.item.toggleOpen(props.index)} + className={classNames( + 'mb-0 flex cursor-pointer items-center justify-between border-t border-white/10 bg-white/10 p-4 text-white', + 'group-[&:first-child]:border-t-0 group-[[open]]:border-b', + '[&::marker]:hidden [&::marker]:content-[""]', + props.item.isOpen && 'border-b [&:first-child]:border-t-0', + )} + > + {props.item.title} +
+ {props.item.isOpen ? : } +
+
+ {props.item.isOpen && ( +
{props.item.renderContent()}
+ )} +
+ ) +} diff --git a/src/components/Account/AccountBalancesTable.tsx b/src/components/Account/AccountBalancesTable.tsx index 24abfa96..75432412 100644 --- a/src/components/Account/AccountBalancesTable.tsx +++ b/src/components/Account/AccountBalancesTable.tsx @@ -21,7 +21,7 @@ interface Props { data: Account } -export const AcccountBalancesTable = (props: Props) => { +export const AccountBalancesTable = (props: Props) => { const displayCurrency = useStore((s) => s.displayCurrency) const prices = useStore((s) => s.prices) const [sorting, setSorting] = React.useState([]) diff --git a/src/components/Account/AccountSummary.tsx b/src/components/Account/AccountSummary.tsx index da5e5208..da49de3c 100644 --- a/src/components/Account/AccountSummary.tsx +++ b/src/components/Account/AccountSummary.tsx @@ -1,11 +1,12 @@ import Accordion from 'components/Accordion' -import { AcccountBalancesTable } from 'components/Account/AccountBalancesTable' +import { AccountBalancesTable } from 'components/Account/AccountBalancesTable' import AccountComposition from 'components/Account/AccountComposition' import AccountHealth from 'components/Account/AccountHealth' import Card from 'components/Card' import DisplayCurrency from 'components/DisplayCurrency' import { ArrowChartLineUp } from 'components/Icons' import Text from 'components/Text' +import useIsOpenArray from 'hooks/useIsOpenArray' import useStore from 'store' import { calculateAccountDeposits } from 'utils/accounts' import { BN } from 'utils/helpers' @@ -16,13 +17,15 @@ interface Props { } export default function AccountSummary(props: Props) { + const [isOpen, toggleOpen] = useIsOpenArray(2, true) + const prices = useStore((s) => s.prices) const baseCurrency = useStore((s) => s.baseCurrency) const accountBalance = props.account ? calculateAccountDeposits(props.account, prices) : BN(0) if (!props.account) return null return ( -
+
, - open: true, + renderContent: () => + props.account ? ( + + ) : null, + isOpen: isOpen[0], + toggleOpen: (index: number) => toggleOpen(index), + }, + { + title: 'Balances', + renderContent: () => + props.account ? : null, + isOpen: isOpen[1], + toggleOpen: (index: number) => toggleOpen(index), }, - { title: 'Balances', content: }, ]} + allowMultipleOpen />
) diff --git a/src/components/Icons/ChevronDown.svg b/src/components/Icons/ChevronDown.svg index fa874741..963bd902 100644 --- a/src/components/Icons/ChevronDown.svg +++ b/src/components/Icons/ChevronDown.svg @@ -1,4 +1,3 @@ - - + + diff --git a/src/components/Icons/ChevronRight.svg b/src/components/Icons/ChevronRight.svg index 0658169e..ecd97cb6 100644 --- a/src/components/Icons/ChevronRight.svg +++ b/src/components/Icons/ChevronRight.svg @@ -1,4 +1,3 @@ - - + + diff --git a/src/components/Modals/BorrowModal.tsx b/src/components/Modals/BorrowModal.tsx index 343fda66..b1360150 100644 --- a/src/components/Modals/BorrowModal.tsx +++ b/src/components/Modals/BorrowModal.tsx @@ -164,6 +164,7 @@ export default function BorrowModal() { amount={amount} max={max} className='w-full' + maxText='Max' /> diff --git a/src/components/Modals/FundAndWithdrawModal.tsx b/src/components/Modals/FundAndWithdrawModal.tsx deleted file mode 100644 index eb350c1f..00000000 --- a/src/components/Modals/FundAndWithdrawModal.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { useEffect, useState } from 'react' - -import AccountSummary from 'components/Account/AccountSummary' -import { Button } from 'components/Button' -import Card from 'components/Card' -import Divider from 'components/Divider' -import { ArrowRight } from 'components/Icons' -import Modal from 'components/Modal' -import Text from 'components/Text' -import TokenInputWithSlider from 'components/TokenInputWithSlider' -import useCurrentAccount from 'hooks/useCurrentAccount' -import useToggle from 'hooks/useToggle' -import useStore from 'store' -import { getAmount } from 'utils/accounts' -import { hardcodedFee } from 'utils/contants' -import { BN } from 'utils/helpers' - -export default function FundAndWithdrawModal() { - const currentAccount = useCurrentAccount() - const modal = useStore((s) => s.fundAndWithdrawModal) - const baseCurrency = useStore((s) => s.baseCurrency) - const withdraw = useStore((s) => s.withdraw) - const deposit = useStore((s) => s.deposit) - const [amount, setAmount] = useState(BN(0)) - const [currentAsset, setCurrentAsset] = useState(baseCurrency) - const [change, setChange] = useState() - const [isConfirming, setIsConfirming] = useToggle() - const balances = useStore((s) => s.balances) - const isFunding = modal === 'fund' - - function resetState() { - setCurrentAsset(baseCurrency) - setAmount(BN(0)) - setChange(undefined) - } - - function onClose() { - resetState() - useStore.setState({ fundAndWithdrawModal: null }) - } - - async function onConfirm() { - if (!currentAccount) return - setIsConfirming(true) - let result - if (isFunding) { - result = await deposit({ - fee: hardcodedFee, - accountId: currentAccount.id, - coin: { - denom: currentAsset.denom, - amount: amount.toString(), - }, - }) - } else { - result = await withdraw({ - fee: hardcodedFee, - accountId: currentAccount.id, - coin: { - denom: currentAsset.denom, - amount: amount.toString(), - }, - }) - } - - setIsConfirming(false) - if (result) { - resetState() - useStore.setState({ fundAndWithdrawModal: null }) - } - } - - const max = isFunding - ? getAmount(currentAsset.denom, balances ?? []) - : currentAccount - ? getAmount(currentAsset.denom, currentAccount.deposits) - : BN(0) - - useEffect(() => { - setChange({ - deposits: [ - { - amount: isFunding ? BN(0).plus(amount).toString() : BN(0).minus(amount).toString(), - denom: currentAsset.denom, - }, - ], - }) - }, [amount, currentAsset, currentAccount, isFunding]) - - return ( - - - {isFunding - ? `Fund Account ${currentAccount?.id ?? '0'}` - : `Withdraw from Account ${currentAccount?.id ?? '0'}`} - - - } - headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' - contentClassName='flex flex-col min-h-[400px]' - > -
- - { - setAmount(val) - }} - onChangeAsset={(asset) => { - setCurrentAsset(asset) - }} - amount={amount} - max={max} - hasSelect - balances={isFunding ? balances : currentAccount?.deposits ?? []} - accountId={!isFunding ? currentAccount?.id ?? '0' : undefined} - /> - -
- - ) -} diff --git a/src/components/Modals/ModalsContainer.tsx b/src/components/Modals/ModalsContainer.tsx index cab43a93..ae094433 100644 --- a/src/components/Modals/ModalsContainer.tsx +++ b/src/components/Modals/ModalsContainer.tsx @@ -1,6 +1,6 @@ import BorrowModal from 'components/Modals/BorrowModal' -import FundAndWithdrawModal from 'components/Modals/FundAndWithdrawModal' -import VaultModal from 'components/Modals/VaultModal' +import FundAndWithdrawModal from 'components/Modals/fundwithdraw/FundAndWithdrawModal' +import VaultModal from 'components/Modals/vault/VaultModal' export default function ModalsContainer() { return ( diff --git a/src/components/Modals/VaultModal.tsx b/src/components/Modals/VaultModal.tsx deleted file mode 100644 index 07298c37..00000000 --- a/src/components/Modals/VaultModal.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { BigNumber } from 'bignumber.js' -import { useState } from 'react' - -import AccountSummary from 'components/Account/AccountSummary' -import { Button } from 'components/Button' -import Card from 'components/Card' -import Divider from 'components/Divider' -import VaultLogo from 'components/Earn/vault/VaultLogo' -import { FormattedNumber } from 'components/FormattedNumber' -import { ArrowRight } from 'components/Icons' -import Modal from 'components/Modal' -import Slider from 'components/Slider' -import Switch from 'components/Switch' -import Text from 'components/Text' -import TitleAndSubCell from 'components/TitleAndSubCell' -import TokenInput from 'components/TokenInput' -import { ASSETS } from 'constants/assets' -import useCurrentAccount from 'hooks/useCurrentAccount' -import useStore from 'store' -import { getAmount } from 'utils/accounts' -import { formatValue } from 'utils/formatters' -import { BN } from 'utils/helpers' - -export default function VaultModal() { - const currentAccount = useCurrentAccount() - const modal = useStore((s) => s.vaultModal) - const [amount, setAmount] = useState(BN(0)) - const [percentage, setPercentage] = useState(0) - const [isCustomAmount, setIsCustomAmount] = useState(false) - - function handleSwitch() { - setIsCustomAmount(() => !isCustomAmount) - } - - function onClose() { - useStore.setState({ vaultModal: null }) - setAmount(BN(0)) - setPercentage(0) - } - - function onChangeSlider(value: number) {} - function onChangePrimary(value: BigNumber) {} - function onChangeSecondary(value: BigNumber) {} - - const primaryAsset = - ASSETS.find((asset) => asset.denom === modal?.vault.denoms.primary) ?? ASSETS[0] - const secondaryAsset = - ASSETS.find((asset) => asset.denom === modal?.vault.denoms.secondary) ?? ASSETS[0] - - const hasValidData = primaryAsset && currentAccount && secondaryAsset - const maxPrimaryAmount = hasValidData - ? getAmount(primaryAsset.denom, currentAccount.deposits) - : BN(0) - const maxSecondaryAmount = hasValidData - ? getAmount(secondaryAsset.denom, currentAccount.deposits) - : BN(0) - - return ( - - - {`${modal.vault.symbols.primary} - ${modal.vault.symbols.secondary}`} - - ) - } - headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' - contentClassName='flex flex-col' - > -
- -
- -
-
- - - - - -
- Custom amount - -
-
- {`${primaryAsset.symbol}-${secondaryAsset.symbol} Position Value`} - -
-
-
- ) -} diff --git a/src/components/Modals/fundwithdraw/FundAndWithdrawModal.tsx b/src/components/Modals/fundwithdraw/FundAndWithdrawModal.tsx new file mode 100644 index 00000000..98ecc583 --- /dev/null +++ b/src/components/Modals/fundwithdraw/FundAndWithdrawModal.tsx @@ -0,0 +1,41 @@ +import Modal from 'components/Modal' +import Text from 'components/Text' +import useCurrentAccount from 'hooks/useCurrentAccount' +import useStore from 'store' +import { CircularProgress } from 'components/CircularProgress' + +import FundWithdrawModalContent from './FundWithdrawModalContent' + +export default function FundAndWithdrawModal() { + const currentAccount = useCurrentAccount() + const modal = useStore((s) => s.fundAndWithdrawModal) + const isFunding = modal === 'fund' + + function onClose() { + useStore.setState({ fundAndWithdrawModal: null }) + } + + return ( + + + {isFunding + ? `Fund Account ${currentAccount?.id ?? '0'}` + : `Withdraw from Account ${currentAccount?.id ?? '0'}`} + + + } + headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' + contentClassName='flex flex-col min-h-[400px]' + > + {modal && currentAccount ? ( + + ) : ( + + )} + + ) +} diff --git a/src/components/Modals/fundwithdraw/FundWithdrawModalContent.tsx b/src/components/Modals/fundwithdraw/FundWithdrawModalContent.tsx new file mode 100644 index 00000000..ed4aa636 --- /dev/null +++ b/src/components/Modals/fundwithdraw/FundWithdrawModalContent.tsx @@ -0,0 +1,113 @@ +import BigNumber from 'bignumber.js' +import { useState } from 'react' + +import AccountSummary from 'components/Account/AccountSummary' +import { Button } from 'components/Button' +import Card from 'components/Card' +import Divider from 'components/Divider' +import { ArrowRight } from 'components/Icons' +import TokenInputWithSlider from 'components/TokenInputWithSlider' +import useToggle from 'hooks/useToggle' +import useStore from 'store' +import { getAmount } from 'utils/accounts' +import { hardcodedFee } from 'utils/contants' +import { BN } from 'utils/helpers' + +interface Props { + account: Account + isFunding: boolean +} + +export default function FundWithdrawModalContent(props: Props) { + const baseCurrency = useStore((s) => s.baseCurrency) + const withdraw = useStore((s) => s.withdraw) + const deposit = useStore((s) => s.deposit) + const balances = useStore((s) => s.balances) + const [isConfirming, setIsConfirming] = useToggle() + const [currentAsset, setCurrentAsset] = useState(baseCurrency) + const [amount, setAmount] = useState(BN(0)) + const [change, setChange] = useState() + + const max = props.isFunding + ? getAmount(currentAsset.denom, balances ?? []) + : props.account + ? getAmount(currentAsset.denom, props.account.deposits) + : BN(0) + + function onChangeAmount(val: BigNumber) { + setAmount(val) + setChange({ + deposits: [ + { + amount: props.isFunding ? BN(0).plus(amount).toString() : BN(0).minus(amount).toString(), + denom: currentAsset.denom, + }, + ], + }) + } + + function resetState() { + setCurrentAsset(baseCurrency) + setAmount(BN(0)) + setChange(undefined) + } + + async function onConfirm() { + setIsConfirming(true) + let result + if (props.isFunding) { + result = await deposit({ + fee: hardcodedFee, + accountId: props.account.id, + coin: { + denom: currentAsset.denom, + amount: amount.toString(), + }, + }) + } else { + result = await withdraw({ + fee: hardcodedFee, + accountId: props.account.id, + coin: { + denom: currentAsset.denom, + amount: amount.toString(), + }, + }) + } + + setIsConfirming(false) + if (result) { + resetState() + useStore.setState({ fundAndWithdrawModal: null }) + } + } + + return ( +
+ + + +
+ ) +} diff --git a/src/components/Modals/vault/VaultBorrowings.tsx b/src/components/Modals/vault/VaultBorrowings.tsx new file mode 100644 index 00000000..450ccb27 --- /dev/null +++ b/src/components/Modals/vault/VaultBorrowings.tsx @@ -0,0 +1,3 @@ +export default function VaultBorrowings() { + return null +} diff --git a/src/components/Modals/vault/VaultDeposit.tsx b/src/components/Modals/vault/VaultDeposit.tsx new file mode 100644 index 00000000..4ca6a9e1 --- /dev/null +++ b/src/components/Modals/vault/VaultDeposit.tsx @@ -0,0 +1,134 @@ +import BigNumber from 'bignumber.js' +import { useState } from 'react' + +import { Button } from 'components/Button' +import Divider from 'components/Divider' +import { FormattedNumber } from 'components/FormattedNumber' +import { ArrowRight } from 'components/Icons' +import Slider from 'components/Slider' +import Switch from 'components/Switch' +import Text from 'components/Text' +import TokenInput from 'components/TokenInput' +import usePrice from 'hooks/usePrice' +import { getAmount } from 'utils/accounts' +import { BN } from 'utils/helpers' + +interface Props { + primaryAsset: Asset + secondaryAsset: Asset + account: Account + onChangeDeposits: (deposits: Map) => void + toggleOpen: (index: number) => void +} + +export default function VaultDeposit(props: Props) { + const [isCustomAmount, setIsCustomAmount] = useState(false) + const [percentage, setPercentage] = useState(0) + const [deposits, setDeposits] = useState>(new Map()) + + const availablePrimaryAmount = getAmount(props.primaryAsset.denom, props.account.deposits) + const availableSecondaryAmount = getAmount(props.secondaryAsset.denom, props.account.deposits) + const primaryPrice = usePrice(props.primaryAsset.denom) + const secondaryPrice = usePrice(props.secondaryAsset.denom) + + const maxAssetValueNonCustom = BN( + Math.min(availablePrimaryAmount.toNumber(), availableSecondaryAmount.toNumber()), + ) + const primaryMax = isCustomAmount + ? availablePrimaryAmount + : maxAssetValueNonCustom.dividedBy(primaryPrice) + const secondaryMax = isCustomAmount + ? availableSecondaryAmount + : maxAssetValueNonCustom.dividedBy(secondaryPrice) + + function handleSwitch() { + const isCustomAmountNew = !isCustomAmount + if (!isCustomAmountNew) { + setDeposits((deposits) => { + deposits.clear() + return new Map(deposits) + }) + setPercentage(0) + } + setIsCustomAmount(isCustomAmountNew) + } + + function onChangePrimaryDeposit(amount: BigNumber) { + onChangeDeposit(props.primaryAsset.denom, amount) + if (!isCustomAmount) { + onChangeDeposit( + props.secondaryAsset.denom, + secondaryMax.multipliedBy(amount.dividedBy(primaryMax)), + ) + } + } + + function onChangeSecondaryDeposit(amount: BigNumber) { + onChangeDeposit(props.secondaryAsset.denom, amount) + if (!isCustomAmount) { + onChangeDeposit( + props.primaryAsset.denom, + primaryMax.multipliedBy(amount.dividedBy(secondaryMax)), + ) + } + } + + function onChangeDeposit(denom: string, amount: BigNumber) { + if (amount.isZero()) { + return setDeposits((deposits) => { + deposits.delete(denom) + return new Map(deposits) + }) + } + + setDeposits((deposits) => { + deposits.set(denom, amount) + props.onChangeDeposits(deposits) + return new Map(deposits) + }) + } + + function onChangeSlider(value: number) { + setPercentage(value) + setDeposits((deposits) => { + deposits.set(props.primaryAsset.denom, primaryMax.multipliedBy(value / 100)) + deposits.set(props.secondaryAsset.denom, secondaryMax.multipliedBy(value / 100)) + return new Map(deposits) + }) + } + + return ( +
+ + {!isCustomAmount && } + + +
+ Custom amount + +
+
+ {`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Position Value`} + +
+
+ ) +} diff --git a/src/components/Modals/vault/VaultModal.tsx b/src/components/Modals/vault/VaultModal.tsx new file mode 100644 index 00000000..0308ac4d --- /dev/null +++ b/src/components/Modals/vault/VaultModal.tsx @@ -0,0 +1,53 @@ +import VaultLogo from 'components/Earn/vault/VaultLogo' +import Modal from 'components/Modal' +import Text from 'components/Text' +import { ASSETS } from 'constants/assets' +import useCurrentAccount from 'hooks/useCurrentAccount' +import useStore from 'store' +import { CircularProgress } from 'components/CircularProgress' + +import VaultModalContent from './VaultModalContent' + +export default function VaultModal() { + const currentAccount = useCurrentAccount() + const modal = useStore((s) => s.vaultModal) + + const primaryAsset = + ASSETS.find((asset) => asset.denom === modal?.vault.denoms.primary) ?? ASSETS[0] + const secondaryAsset = + ASSETS.find((asset) => asset.denom === modal?.vault.denoms.secondary) ?? ASSETS[0] + + const hasValidData = primaryAsset && currentAccount && secondaryAsset + + function onClose() { + useStore.setState({ vaultModal: null }) + } + + return ( + + + {`${modal.vault.symbols.primary} - ${modal.vault.symbols.secondary}`} + + ) + } + headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' + contentClassName='flex flex-col' + > + {modal?.vault && currentAccount ? ( + + ) : ( + + )} + + ) +} diff --git a/src/components/Modals/vault/VaultModalContent.tsx b/src/components/Modals/vault/VaultModalContent.tsx new file mode 100644 index 00000000..45108289 --- /dev/null +++ b/src/components/Modals/vault/VaultModalContent.tsx @@ -0,0 +1,52 @@ +import BigNumber from 'bignumber.js' +import { useState } from 'react' + +import Accordion from 'components/Accordion' +import AccountSummary from 'components/Account/AccountSummary' +import useIsOpenArray from 'hooks/useIsOpenArray' + +import VaultDeposit from './VaultDeposit' +import VaultBorrowings from './VaultBorrowings' + +interface Props { + vault: Vault + primaryAsset: Asset + secondaryAsset: Asset + account: Account +} + +export default function VaultModalContent(props: Props) { + const [isOpen, toggleOpen] = useIsOpenArray(2, false) + const [deposits, setDeposits] = useState>(new Map()) + + return ( +
+ ( + setDeposits(deposits)} + primaryAsset={props.primaryAsset} + secondaryAsset={props.secondaryAsset} + account={props.account} + toggleOpen={toggleOpen} + /> + ), + title: 'Deposit', + isOpen: isOpen[0], + toggleOpen: (index: number) => toggleOpen(index), + }, + { + renderContent: () => , + title: 'Borrow', + isOpen: isOpen[1], + toggleOpen: (index: number) => toggleOpen(index), + }, + ]} + /> + + +
+ ) +} diff --git a/src/components/Portfolio/AccountOverview.tsx b/src/components/Portfolio/AccountOverview.tsx index bcfc6ec8..fb4fb8c1 100644 --- a/src/components/Portfolio/AccountOverview.tsx +++ b/src/components/Portfolio/AccountOverview.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames' import { Suspense } from 'react' import { useParams } from 'react-router-dom' -import { AcccountBalancesTable } from 'components/Account/AccountBalancesTable' +import { AccountBalancesTable } from 'components/Account/AccountBalancesTable' import AccountComposition from 'components/Account/AccountComposition' import Card from 'components/Card' import Loading from 'components/Loading' @@ -35,7 +35,7 @@ function Content() { Balances - + ))}
diff --git a/src/components/TokenInput.tsx b/src/components/TokenInput.tsx index cb392214..a1ba7e2a 100644 --- a/src/components/TokenInput.tsx +++ b/src/components/TokenInput.tsx @@ -9,8 +9,9 @@ import Select from 'components/Select/Select' import Text from 'components/Text' import { ASSETS } from 'constants/assets' import useStore from 'store' -import { magnify } from 'utils/formatters' import { BN } from 'utils/helpers' +import { FormattedNumber } from 'components/FormattedNumber' +import { Button } from 'components/Button' interface Props { amount: BigNumber @@ -24,6 +25,7 @@ interface Props { interface SingleProps extends Props { asset: Asset max: BigNumber + maxText: string hasSelect?: boolean onChangeAsset?: (asset: Asset, max: BigNumber) => void } @@ -31,6 +33,7 @@ interface SingleProps extends Props { interface SelectProps extends Props { asset?: Asset max?: BigNumber + maxText?: string hasSelect: boolean onChangeAsset: (asset: Asset, max: BigNumber) => void } @@ -43,7 +46,10 @@ export default function TokenInput(props: SingleProps | SelectProps) { amount: '0', }) - const selectedAssetDenom = props.asset ? props.asset.denom : baseCurrency.denom + // TODO: Refactor the useEffect + useEffect(() => { + props.onChangeAsset && props.onChangeAsset(asset, coin ? BN(coin.amount) : BN(0)) + }, [coin, asset]) const updateAsset = useCallback( (coinDenom: string) => { @@ -55,29 +61,11 @@ export default function TokenInput(props: SingleProps | SelectProps) { [props.balances, baseCurrency], ) - function setDefaultAsset() { - if (!props.balances || props.balances?.length === 0) return setAsset(baseCurrency) - if (props.balances.length === 1) { - const balances = props.balances ?? [] - return setAsset(ASSETS.find((asset) => asset.denom === balances[0].denom) ?? baseCurrency) - } - return setAsset(ASSETS.find((asset) => asset.denom === selectedAssetDenom) ?? baseCurrency) + function onMaxBtnClick() { + if (!props.max) return + props.onChange(BN(props.max)) } - useEffect( - () => { - setDefaultAsset() - updateAsset(asset.denom) - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ) - - useEffect(() => { - props.onChangeAsset && props.onChangeAsset(asset, coin ? BN(coin.amount) : BN(0)) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [coin, asset]) - return (
-
- - {`1 ${asset.symbol} =`} - - +
+ {props.max && props.maxText && ( + <> + + {`${props.maxText}:`} + + + + + )}
void @@ -24,6 +25,7 @@ interface SingleProps extends Props { interface SelectProps extends Props { max?: BigNumber + maxText?: string asset?: Asset onChangeAsset: (asset: Asset) => void hasSelect: boolean @@ -82,6 +84,7 @@ export default function TokenInputWithSlider(props: SingleProps | SelectProps) { onChangeAsset={(asset: Asset, max: BigNumber) => onAssetChange(asset, max)} amount={amount} max={max} + maxText={props.maxText || ''} className='mb-4' disabled={props.disabled} hasSelect={props.hasSelect} diff --git a/src/hooks/useIsOpenArray.tsx b/src/hooks/useIsOpenArray.tsx new file mode 100644 index 00000000..67048e34 --- /dev/null +++ b/src/hooks/useIsOpenArray.tsx @@ -0,0 +1,16 @@ +import { useState } from 'react' + +export default function useIsOpenArray(length: number, allowMultiple: boolean) { + const [isOpen, setIsOpen] = useState(Array.from({ length }, (_, i) => i === 0)) + + function toggleOpen(index: number) { + setIsOpen((prev) => { + return prev.map((_, i) => { + if (i === index) return !prev[i] + return allowMultiple ? prev[i] : false + }) + }) + } + + return [isOpen, toggleOpen] as const +} diff --git a/src/hooks/usePrice.tsx b/src/hooks/usePrice.tsx new file mode 100644 index 00000000..89918301 --- /dev/null +++ b/src/hooks/usePrice.tsx @@ -0,0 +1,7 @@ +import usePrices from './usePrices' + +export default function usePrice(denom: string) { + const { data: prices } = usePrices() + + return prices.find((coin) => coin.denom === denom)?.amount ?? 0 +}