MP-3128: added conditional ActionButtons (#324)

* MP-3128: added conditional ActionButtons

* feat: added Create Account button

* fix: simplified the ActionsButton according to feedback

* fix: moved the onClick handler to the card of an account summary instead of the title

* fix: fixed the health and balance display bug

* fix: remove Leverage static number

* fix: small style fix
This commit is contained in:
Linkie Link 2023-07-28 11:25:09 +02:00 committed by GitHub
parent bd07c20e47
commit fec03d030f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 115 additions and 70 deletions

View File

@ -51,14 +51,6 @@ function AccountDetails(props: Props) {
/>
</div>
<div className='w-full border border-x-0 border-white/20 py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Leverage
</Text>
<Text size='xs' className='w-full text-center'>
4.5x
</Text>
</div>
<div className='w-full px-1 py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Balance
</Text>

View File

@ -10,14 +10,10 @@ import { ArrowCircledTopRight, ArrowDownLine, ArrowUpLine, TrashBin } from 'comp
import Radio from 'components/Radio'
import SwitchWithLabel from 'components/SwitchWithLabel'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import { BN_ZERO } from 'constants/math'
import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { calculateAccountBalanceValue, calculateAccountDepositsValue } from 'utils/accounts'
import { calculateAccountDepositsValue } from 'utils/accounts'
import { hardcodedFee } from 'utils/constants'
import { getPage, getRoute } from 'utils/route'
@ -28,6 +24,7 @@ interface Props {
const accountCardHeaderClasses = classNames(
'flex w-full items-center justify-between bg-white/20 px-4 py-2.5 text-white/70',
'border border-transparent border-b-white/20',
'group-hover/account:bg-white/30',
)
export default function AccountList(props: Props) {
@ -38,10 +35,6 @@ export default function AccountList(props: Props) {
const { autoLendEnabledAccountIds, toggleAutoLend } = useAutoLendEnabledAccountIds()
const deleteAccount = useStore((s) => s.deleteAccount)
const accountSelected = !!accountId && !isNaN(Number(accountId))
const selectedAccountDetails = props.accounts.find((account) => account.id === accountId)
const selectedAccountBalance = selectedAccountDetails
? calculateAccountBalanceValue(selectedAccountDetails, prices)
: BN_ZERO
async function deleteAccountHandler() {
if (!accountSelected) return
@ -72,31 +65,25 @@ export default function AccountList(props: Props) {
<Card
id={`account-${account.id}`}
key={account.id}
className='w-full'
contentClassName='bg-white/10'
className={classNames('w-full', !isActive && 'group/account hover:cursor-pointer')}
contentClassName='bg-white/10 group-hover/account:bg-white/20'
onClick={() => {
if (isActive) return
navigate(getRoute(getPage(pathname), address, account.id))
}}
title={
<div
className={classNames(
accountCardHeaderClasses,
!isActive && 'group hover:cursor-pointer',
)}
role={!isActive ? 'button' : undefined}
onClick={() => {
if (isActive) return
navigate(getRoute(getPage(pathname), address, account.id))
}}
>
<div className={accountCardHeaderClasses} role={!isActive ? 'button' : undefined}>
<Text size='xs' className='flex flex-1'>
Credit Account {account.id}
</Text>
<Radio active={isActive} className='group-hover:opacity-100' />
<Radio active={isActive} className='group-hover/account:opacity-100' />
</div>
}
>
{isActive ? (
<>
<div className='w-full border border-transparent border-b-white/20 p-4'>
<AccountStats balance={selectedAccountBalance} risk={75} health={85} />
<AccountStats account={account} />
</div>
<div className='grid grid-flow-row grid-cols-2 gap-4 p-4'>
<Button
@ -149,7 +136,7 @@ export default function AccountList(props: Props) {
</>
) : (
<div className='w-full p-4'>
<AccountStats balance={positionBalance} risk={60} health={50} />
<AccountStats account={account} />
</div>
)}
</Card>

View File

@ -1,25 +1,30 @@
import BigNumber from 'bignumber.js'
import AccountHealth from 'components/Account/AccountHealth'
import DisplayCurrency from 'components/DisplayCurrency'
import { ORACLE_DENOM } from 'constants/oracle'
import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountDepositsValue } from 'utils/accounts'
import { formatHealth } from 'utils/formatters'
import { BN } from 'utils/helpers'
interface Props {
balance: BigNumber
risk: number
health: number
account: Account
}
export default function AccountStats(props: Props) {
const { data: prices } = usePrices()
const positionBalance = calculateAccountDepositsValue(props.account, prices)
const { health } = useHealthComputer(props.account)
const healthFactor = BN(100).minus(formatHealth(health)).toNumber()
return (
<div className='w-full flex-wrap'>
<DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, props.balance)}
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionBalance)}
className='w-full text-xl'
/>
<div className='mt-1 flex w-full items-center'>
<AccountHealth health={props.health} classNames='w-[140px]' hasLabel />
<AccountHealth health={healthFactor} classNames='w-[140px]' hasLabel />
</div>
</div>
)

View File

@ -1,6 +1,7 @@
import { useCallback } from 'react'
import Button from 'components/Button'
import ActionButton from 'components/Button/ActionButton'
import { Plus, ReceiptCheck } from 'components/Icons'
import useStore from 'store'
import { getEnabledMarketAssets } from 'utils/assets'
@ -28,7 +29,7 @@ export default function BorrowActionButtons(props: Props) {
return (
<div className='flex flex-row space-x-2'>
<Button
<ActionButton
leftIcon={<Plus className='w-3' />}
onClick={borrowHandler}
color='secondary'

View File

@ -0,0 +1,46 @@
import { useCallback } from 'react'
import { useParams } from 'react-router-dom'
import AccountCreateFirst from 'components/Account/AccountCreateFirst'
import { ACCOUNT_MENU_BUTTON_ID } from 'components/Account/AccountMenuContent'
import Button from 'components/Button'
import { Account, PlusCircled } from 'components/Icons'
import WalletConnectButton from 'components/Wallet/WalletConnectButton'
import useStore from 'store'
export default function ActionButton(props: ButtonProps) {
const { className, color, variant, size } = props
const defaultProps = { className, color, variant, size }
const address = useStore((s) => s.address)
const accounts = useStore((s) => s.accounts)
const { accountId } = useParams()
const handleCreateAccountClick = useCallback(() => {
useStore.setState({ focusComponent: <AccountCreateFirst /> })
}, [])
if (!address) return <WalletConnectButton {...defaultProps} />
if (accounts && accounts.length === 0)
return (
<Button
onClick={handleCreateAccountClick}
leftIcon={<PlusCircled />}
text='Create Account'
{...defaultProps}
/>
)
if (!accountId)
return (
<Button
text='Select Account'
onClick={() => document.getElementById(ACCOUNT_MENU_BUTTON_ID)?.click()}
leftIcon={<Account />}
rightIcon={undefined}
{...defaultProps}
/>
)
return <Button {...props} />
}

View File

@ -1,6 +1,6 @@
import classNames from 'classnames'
export function glowElement(enableAnimations: boolean) {
export function glowElement(enableAnimations: boolean, className?: string) {
return (
<svg
className={classNames(

View File

@ -7,6 +7,7 @@ interface Props {
children: ReactNode
className?: string
contentClassName?: string
onClick?: () => void
title?: string | ReactElement
id?: string
}
@ -15,6 +16,7 @@ export default function Card(props: Props) {
return (
<section
id={props.id}
onClick={props.onClick}
className={classNames(
props.className,
'flex flex-col',

View File

@ -10,7 +10,7 @@ import {
import classNames from 'classnames'
import React from 'react'
import Button from 'components/Button'
import ActionButton from 'components/Button/ActionButton'
import DisplayCurrency from 'components/DisplayCurrency'
import VaultExpanded from 'components/Earn/Farm/VaultExpanded'
import VaultLogo from 'components/Earn/Farm/VaultLogo'
@ -205,9 +205,7 @@ export const VaultTable = (props: Props) => {
<ChevronDown />
</div>
) : (
<Button onClick={enterVaultHandler} color='tertiary'>
Deposit
</Button>
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
)}
</div>
)

View File

@ -1,7 +1,9 @@
import { useCallback } from 'react'
import { useParams } from 'react-router-dom'
import { ACCOUNT_MENU_BUTTON_ID } from 'components/Account/AccountMenuContent'
import Button from 'components/Button'
import ActionButton from 'components/Button/ActionButton'
import { ArrowDownLine, ArrowUpLine, Enter } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
@ -10,6 +12,7 @@ import useAlertDialog from 'hooks/useAlertDialog'
import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useCurrentAccountDeposits from 'hooks/useCurrentAccountDeposits'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
@ -26,6 +29,9 @@ export default function LendingActionButtons(props: Props) {
const { open: showAlertDialog } = useAlertDialog()
const { isAutoLendEnabledForCurrentAccount } = useAutoLendEnabledAccountIds()
const assetDepositAmount = accountDeposits.find(byDenom(asset.denom))?.amount
const address = useStore((s) => s.address)
const { accountId } = useParams()
const hasNoDeposit = !!(!assetDepositAmount && address && accountId)
const handleWithdraw = useCallback(() => {
if (isAutoLendEnabledForCurrentAccount) {
@ -64,7 +70,7 @@ export default function LendingActionButtons(props: Props) {
)}
<ConditionalWrapper
condition={!assetDepositAmount}
condition={hasNoDeposit}
wrapper={(children) => (
<Tooltip
type='warning'
@ -76,16 +82,15 @@ export default function LendingActionButtons(props: Props) {
</Tooltip>
)}
>
<Button
<ActionButton
leftIcon={<ArrowUpLine />}
iconClassName={iconClassnames}
disabled={!assetDepositAmount}
disabled={hasNoDeposit}
color='secondary'
onClick={() => openLend(props.data)}
className={buttonClassnames}
>
Lend
</Button>
text='Lend'
/>
</ConditionalWrapper>
</div>
)

View File

@ -7,11 +7,11 @@ import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets'
interface Props {
defaultSelectedDenoms: string[]
onChangeDenoms: (denoms: string[]) => void
}
export default function WalletAssetsModalContent(props: Props) {
const { onChangeDenoms } = props
const [searchString, setSearchString] = useState<string>('')
const balances = useStore((s) => s.balances)
@ -39,12 +39,12 @@ export default function WalletAssetsModalContent(props: Props) {
currentSelectedDenom?.filter((denom) => filteredAssets.findIndex(byDenom(denom))) || [],
)
const onChangeDenoms = useCallback(
const onChangeSelect = useCallback(
(denoms: string[]) => {
setSelectedDenoms(denoms)
props.onChangeDenoms(denoms)
onChangeDenoms(denoms)
},
[props.onChangeDenoms],
[onChangeDenoms],
)
return (
@ -59,7 +59,7 @@ export default function WalletAssetsModalContent(props: Props) {
<div className='max-h-[446px] overflow-y-scroll scrollbar-hide'>
<AssetSelectTable
assets={filteredAssets}
onChangeSelected={onChangeDenoms}
onChangeSelected={onChangeSelect}
selectedDenoms={selectedDenoms}
/>
</div>

View File

@ -25,10 +25,7 @@ export default function WalletAssetsModal() {
modalClassName='max-w-modal-xs'
headerClassName='bg-white/10 border-b-white/5 border-b items-center p-4'
>
<WalletAssetsModalContent
defaultSelectedDenoms={modal.selectedDenoms}
onChangeDenoms={setSelectedDenoms}
/>
<WalletAssetsModalContent onChangeDenoms={setSelectedDenoms} />
<div className='flex w-full p-4'>
<Button className='w-full' onClick={onClose} color='tertiary' text='Select Assets' />
</div>

View File

@ -1,11 +1,11 @@
import classNames from 'classnames'
import { useCallback, useMemo, useState } from 'react'
import Button from 'components/Button'
import ActionButton from 'components/Button/ActionButton'
import useSwapRoute from 'hooks/useSwapRoute'
import { getAssetByDenom } from 'utils/assets'
import { hardcodedFee } from 'utils/constants'
import { formatAmountWithSymbol } from 'utils/formatters'
import { getAssetByDenom } from 'utils/assets'
import useSwapRoute from 'hooks/useSwapRoute'
interface Props {
buyAsset: Asset
@ -53,12 +53,14 @@ export default function TradeSummary(props: Props) {
<span>{parsedRoutes}</span>
</div>
</div>
<Button
<ActionButton
disabled={routes.length === 0 || buyButtonDisabled}
showProgressIndicator={isButtonBusy || isRouteLoading}
text={buttonText}
onClick={handleBuyClick}
size='md'
color='primary'
className='w-full'
/>
</div>
)

View File

@ -11,6 +11,10 @@ import useStore from 'store'
interface Props {
textOverride?: string | ReactNode
disabled?: boolean
className?: string
color?: ButtonProps['color']
variant?: ButtonProps['variant']
size?: ButtonProps['size']
}
export default function WalletConnectButton(props: Props) {
@ -24,14 +28,15 @@ export default function WalletConnectButton(props: Props) {
return (
<div className='relative'>
<Button
variant='solid'
color='tertiary'
variant={props.variant ?? 'solid'}
color={props.color ?? 'tertiary'}
size={props.size ?? 'sm'}
disabled={props.disabled}
onClick={handleClick}
leftIcon={<Wallet />}
>
<span>{props.textOverride || 'Connect Wallet'}</span>
</Button>
className={props.className}
text={props.textOverride ?? 'Connect Wallet'}
/>
</div>
)
}

View File

@ -3,6 +3,7 @@ import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useCallback, useEffect, useState } from 'react'
import useClipboard from 'react-use-clipboard'
import { useLocation, useNavigate } from 'react-router-dom'
import Button from 'components/Button'
import { CircularProgress } from 'components/CircularProgress'
@ -20,6 +21,7 @@ import useStore from 'store'
import { ChainInfoID } from 'types/enums/wallet'
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
import { truncate } from 'utils/formatters'
import { getPage, getRoute } from 'utils/route'
export default function WalletConnectedButton() {
// ---------------
@ -33,6 +35,8 @@ export default function WalletConnectedButton() {
const network = useStore((s) => s.client?.connectedWallet.network)
const baseAsset = getBaseAsset()
const { data: walletBalances, isLoading } = useWalletBalances(address)
const navigate = useNavigate()
const { pathname } = useLocation()
// ---------------
// LOCAL STATE
@ -57,6 +61,7 @@ export default function WalletConnectedButton() {
if (!currentWallet) return
disconnectWallet(currentWallet)
useStore.setState({ client: undefined, balances: [] })
navigate(getRoute(getPage(pathname)))
}
useEffect(() => {