mars-v2-frontend/src/components/Account/AccountDetails/index.tsx
Linkie Link a160f495e3
Healthfactor adjustments (#594)
* fix: do update the health on sliding the margin back to 0

* MP-3531: first updates on the health bars

* fix: added exponential function for health percentage

* fix: build fix

* tidy: refactor

* tidy: cleanup

* feat: added new curve

* fix: base set to 3.5

* env: version update
2023-10-27 18:24:13 +02:00

197 lines
7.5 KiB
TypeScript

import classNames from 'classnames'
import { useCallback, useMemo } from 'react'
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
import AccountDetailsLeverage from 'components/Account/AccountDetails/AccountDetailsLeverage'
import Skeleton from 'components/Account/AccountDetails/Skeleton'
import { HealthGauge } from 'components/Account/Health/HealthGauge'
import EscButton from 'components/Button/EscButton'
import { glowElement } from 'components/Button/utils'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { ThreeDots } from 'components/Icons'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle'
import useAccountId from 'hooks/useAccountId'
import useAccountIds from 'hooks/useAccountIds'
import useAccounts from 'hooks/useAccounts'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import {
calculateAccountApr,
calculateAccountBalanceValue,
calculateAccountLeverage,
} from 'utils/accounts'
export default function AccountDetailsController() {
const address = useStore((s) => s.address)
const { data: accounts, isLoading } = useAccounts(address)
const { data: accountIds } = useAccountIds(address, false)
const accountId = useAccountId()
const account = useCurrentAccount()
const focusComponent = useStore((s) => s.focusComponent)
const isOwnAccount = accountId && accountIds?.includes(accountId)
if (!address || focusComponent || !isOwnAccount) return null
if ((isLoading && accountId && !focusComponent) || !account) return <Skeleton />
return <AccountDetails account={account} />
}
interface Props {
account: Account
}
function AccountDetails(props: Props) {
const { account } = props
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
)
const updatedAccount = useStore((s) => s.updatedAccount)
const accountDetailsExpanded = useStore((s) => s.accountDetailsExpanded)
const { health, healthFactor } = useHealthComputer(account)
const { health: updatedHealth, healthFactor: updatedHealthFactor } = useHealthComputer(
updatedAccount || account,
)
const { data: prices } = usePrices()
const accountBalanceValue = useMemo(
() => calculateAccountBalanceValue(updatedAccount ?? account, prices),
[updatedAccount, account, prices],
)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue)
const leverage = useMemo(() => calculateAccountLeverage(account, prices), [account, prices])
const updatedLeverage = useMemo(() => {
if (!updatedAccount) return null
const updatedLeverage = calculateAccountLeverage(updatedAccount, prices)
if (updatedLeverage.eq(leverage)) return null
return updatedLeverage
}, [updatedAccount, prices, leverage])
const { data } = useBorrowMarketAssetsTableData(false)
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const apr = useMemo(
() =>
calculateAccountApr(updatedAccount ?? account, borrowAssetsData, lendingAssetsData, prices),
[account, borrowAssetsData, lendingAssetsData, prices, updatedAccount],
)
function AccountDetailsHeader() {
const onClose = useCallback(() => useStore.setState({ accountDetailsExpanded: false }), [])
return (
<div className='flex items-center justify-between w-full p-4 bg-white/10 '>
<Text size='lg' className='flex items-center flex-grow font-semibold'>
{`Credit Account ${account.id}`}
</Text>
<EscButton onClick={onClose} hideText className='w-6 h-6' />
</div>
)
}
return (
<div
data-testid='account-details'
className={classNames(
accountDetailsExpanded ? 'right-4' : '-right-90',
'w-110 flex items-start gap-4 absolute top-6',
!reduceMotion && 'transition-all duration-300',
)}
>
<div
className={classNames(
'flex flex-wrap min-w-16 w-16 group relative',
'border rounded-base border-white/20',
'bg-white/5 backdrop-blur-sticky',
!reduceMotion && 'transition-colors duration-300',
'hover:bg-white/10 hover:cursor-pointer ',
)}
onClick={() => useStore.setState({ accountDetailsExpanded: !accountDetailsExpanded })}
>
<div className='flex flex-wrap justify-center w-full py-4'>
<HealthGauge
health={health}
updatedHealth={updatedHealth}
healthFactor={healthFactor}
updatedHealthFactor={updatedHealthFactor}
/>
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
Health
</Text>
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50 whitespace-nowrap'>
Net worth
</Text>
<DisplayCurrency coin={coin} className='w-full text-center truncate text-2xs ' />
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Leverage
</Text>
<AccountDetailsLeverage
leverage={leverage.toNumber() || 1}
updatedLeverage={updatedLeverage?.toNumber() || null}
/>
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
APR
</Text>
<FormattedNumber
className={'w-full text-center text-2xs'}
amount={apr.toNumber()}
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
animate
/>
</div>
<div
className={classNames(
'flex justify-center items-center w-full h-6 opacity-50',
!reduceMotion && 'transition-[opacity] duration-300',
'absolute -bottom-6',
'group-hover:opacity-100',
)}
>
{!accountDetailsExpanded && <ThreeDots className='h-1' />}
</div>
{glowElement(!reduceMotion)}
</div>
<div className='flex w-90 backdrop-blur-sticky'>
<Card className='w-90 bg-white/5' title={<AccountDetailsHeader />}>
<AccountComposition account={account} />
<Text className='w-full px-4 py-2 text-white bg-white/10'>Balances</Text>
<AccountBalancesTable
account={account}
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
hideCard
/>
</Card>
</div>
</div>
)
}