mango-v4-ui/components/account/HealthContributions.tsx

251 lines
8.8 KiB
TypeScript
Raw Normal View History

2023-07-06 20:45:32 -07:00
import HealthContributionsChart from './HealthContributionsChart'
import useMangoGroup from 'hooks/useMangoGroup'
import useMangoAccount from 'hooks/useMangoAccount'
2023-07-10 23:01:22 -07:00
import { useMemo, useState } from 'react'
2023-07-06 20:45:32 -07:00
import { HealthType } from '@blockworks-foundation/mango-v4'
2023-07-10 23:01:22 -07:00
import {
ArrowLeftIcon,
QuestionMarkCircleIcon,
} from '@heroicons/react/20/solid'
2023-07-10 04:56:38 -07:00
import Tooltip from '@components/shared/Tooltip'
import TokenLogo from '@components/shared/TokenLogo'
import { useTranslation } from 'next-i18next'
import MarketLogos from '@components/trade/MarketLogos'
2023-07-10 23:01:22 -07:00
import mangoStore from '@store/mangoStore'
2023-07-11 04:00:06 -07:00
import TokensHealthTable from './TokensHealthTable'
import MarketsHealthTable from './MarketsHealthTable'
2023-07-06 20:45:32 -07:00
export interface HealthContribution {
asset: string
contribution: number
isAsset: boolean
}
const HealthContributions = ({ hideView }: { hideView: () => void }) => {
2023-07-10 05:39:50 -07:00
const { t } = useTranslation(['common', 'account', 'trade'])
2023-07-06 20:45:32 -07:00
const { group } = useMangoGroup()
const { mangoAccount } = useMangoAccount()
2023-07-10 23:01:22 -07:00
const [initActiveIndex, setInitActiveIndex] = useState<number | undefined>(
undefined
)
const [maintActiveIndex, setMaintActiveIndex] = useState<number | undefined>(
undefined
)
2023-07-06 20:45:32 -07:00
const [initHealthContributions, maintHealthContributions] = useMemo(() => {
if (!group || !mangoAccount) return [[], []]
const init = mangoAccount
.getHealthContributionPerAssetUi(group, HealthType.init)
.map((item) => ({
...item,
contribution: Math.abs(item.contribution),
isAsset: item.contribution > 0 ? true : false,
}))
const maint = mangoAccount
.getHealthContributionPerAssetUi(group, HealthType.maint)
.map((item) => ({
...item,
contribution: Math.abs(item.contribution),
isAsset: item.contribution > 0 ? true : false,
}))
return [init, maint]
}, [group, mangoAccount])
2023-07-10 04:56:38 -07:00
const [initHealthMarkets, initHealthTokens] = useMemo(() => {
if (!initHealthContributions.length) return [[], []]
const splitData = initHealthContributions.reduce(
(
acc: { market: HealthContribution[]; token: HealthContribution[] },
obj: HealthContribution
) => {
if (obj.asset.includes('/')) {
acc.market.push(obj)
} else {
acc.token.push(obj)
}
return acc
},
{ market: [], token: [] }
)
return [splitData.market, splitData.token]
}, [initHealthContributions])
2023-07-06 20:45:32 -07:00
2023-07-10 04:56:38 -07:00
const [maintHealthMarkets, maintHealthTokens] = useMemo(() => {
if (!maintHealthContributions.length) return [[], []]
const splitData = maintHealthContributions.reduce(
(
acc: { market: HealthContribution[]; token: HealthContribution[] },
obj: HealthContribution
) => {
if (obj.asset.includes('/')) {
acc.market.push(obj)
} else {
acc.token.push(obj)
}
return acc
},
{ market: [], token: [] }
)
2023-07-10 06:02:29 -07:00
const markets = splitData.market.filter((d) => d.contribution > 0)
const tokens = splitData.token
return [markets, tokens]
2023-07-10 04:56:38 -07:00
}, [maintHealthContributions])
2023-07-10 23:01:22 -07:00
const handleLegendClick = (item: HealthContribution) => {
const maintIndex = maintChartData.findIndex((d) => d.asset === item.asset)
const initIndex = initChartData.findIndex((d) => d.asset === item.asset)
setMaintActiveIndex(maintIndex)
setInitActiveIndex(initIndex)
}
const handleLegendMouseEnter = (item: HealthContribution) => {
const maintIndex = maintChartData.findIndex((d) => d.asset === item.asset)
const initIndex = initChartData.findIndex((d) => d.asset === item.asset)
setMaintActiveIndex(maintIndex)
setInitActiveIndex(initIndex)
}
const handleLegendMouseLeave = () => {
setInitActiveIndex(undefined)
setMaintActiveIndex(undefined)
}
const renderLegendLogo = (asset: string) => {
const group = mangoStore.getState().group
if (!group)
return <QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
const isSpotMarket = asset.includes('/')
if (isSpotMarket) {
const market = group.getSerum3MarketByName(asset)
return market ? (
<MarketLogos market={market} size="small" />
) : (
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
)
} else {
const bank = group.banksMapByName.get(asset)?.[0]
return bank ? (
<div className="mr-1.5">
<TokenLogo bank={bank} size={16} />
</div>
) : (
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
)
}
}
const initChartData = useMemo(() => {
if (!initHealthContributions.length) return []
return initHealthContributions
.filter((cont) => {
if (cont.asset.includes('/')) {
return cont.contribution > 0.01
} else return cont
})
.sort((a, b) => {
const aMultiplier = a.isAsset ? 1 : -1
const bMultiplier = b.isAsset ? 1 : -1
return b.contribution * bMultiplier - a.contribution * aMultiplier
})
}, [initHealthContributions])
const maintChartData = useMemo(() => {
if (!maintHealthContributions.length) return []
return maintHealthContributions
.filter((cont) => {
if (cont.asset.includes('/')) {
return cont.contribution > 0.01
} else return cont
})
.sort((a, b) => {
const aMultiplier = a.isAsset ? 1 : -1
const bMultiplier = b.isAsset ? 1 : -1
return b.contribution * bMultiplier - a.contribution * aMultiplier
})
}, [maintHealthContributions])
2023-07-10 04:56:38 -07:00
return group && mangoAccount ? (
2023-07-06 20:45:32 -07:00
<>
2023-07-10 04:56:38 -07:00
<div className="hide-scroll flex h-14 items-center space-x-4 overflow-x-auto border-b border-th-bkg-3">
2023-07-06 20:45:32 -07:00
<button
className="flex h-14 w-14 flex-shrink-0 items-center justify-center border-r border-th-bkg-3 focus-visible:bg-th-bkg-3 md:hover:bg-th-bkg-2"
onClick={hideView}
>
<ArrowLeftIcon className="h-5 w-5" />
</button>
2023-07-10 23:01:22 -07:00
<h2 className="text-lg">{t('account:health-contributions')}</h2>
2023-07-10 04:56:38 -07:00
</div>
2023-07-10 23:01:22 -07:00
<div className="mx-auto grid max-w-[1140px] grid-cols-2 gap-6 p-6 sm:gap-8">
2023-07-10 04:56:38 -07:00
<div className="col-span-1 flex h-full flex-col items-center">
<Tooltip content={t('account:tooltip-init-health')}>
2023-07-10 23:01:22 -07:00
<h3 className="tooltip-underline text-xs sm:text-base">
{t('account:init-health-contributions')}
2023-07-10 04:56:38 -07:00
</h3>
</Tooltip>
2023-07-10 23:01:22 -07:00
<HealthContributionsChart
data={initChartData}
activeIndex={initActiveIndex}
setActiveIndex={setInitActiveIndex}
/>
2023-07-10 04:56:38 -07:00
</div>
<div className="col-span-1 flex flex-col items-center">
<Tooltip content={t('account:tooltip-maint-health')}>
2023-07-10 23:01:22 -07:00
<h3 className="tooltip-underline text-xs sm:text-base">
{t('account:maint-health-contributions')}
2023-07-10 04:56:38 -07:00
</h3>
</Tooltip>
2023-07-10 23:01:22 -07:00
<HealthContributionsChart
data={maintChartData}
activeIndex={maintActiveIndex}
setActiveIndex={setMaintActiveIndex}
/>
</div>
<div className="col-span-2 mx-auto flex max-w-[600px] flex-wrap justify-center space-x-4">
{[...maintChartData]
.sort((a, b) => b.contribution - a.contribution)
.map((d, i) => {
return (
<div
key={d.asset + i}
className={`default-transition flex h-7 cursor-pointer items-center md:hover:text-th-active`}
onClick={() => handleLegendClick(d)}
onMouseEnter={() => handleLegendMouseEnter(d)}
onMouseLeave={handleLegendMouseLeave}
>
{renderLegendLogo(d.asset)}
<span className={`default-transition`}>{d.asset}</span>
</div>
)
})}
2023-07-10 04:56:38 -07:00
</div>
2023-07-06 20:45:32 -07:00
</div>
2023-07-10 04:56:38 -07:00
{maintHealthTokens.length ? (
2023-07-10 23:01:22 -07:00
<div className="border-t border-th-bkg-3 pt-6">
2023-07-10 04:56:38 -07:00
<h2 className="mb-1 px-6 text-lg">{t('tokens')}</h2>
2023-07-11 04:00:06 -07:00
<TokensHealthTable
initTokens={initHealthTokens}
maintTokens={maintHealthTokens}
handleLegendClick={handleLegendClick}
handleLegendMouseEnter={handleLegendMouseEnter}
handleLegendMouseLeave={handleLegendMouseLeave}
/>
2023-07-10 04:56:38 -07:00
</div>
) : null}
{maintHealthMarkets.length ? (
2023-07-10 23:01:22 -07:00
<div className="pt-6">
2023-07-10 04:56:38 -07:00
<h2 className="mb-1 px-6 text-lg">{t('markets')}</h2>
2023-07-11 04:00:06 -07:00
<MarketsHealthTable
initMarkets={initHealthMarkets}
maintMarkets={maintHealthMarkets}
handleLegendClick={handleLegendClick}
handleLegendMouseEnter={handleLegendMouseEnter}
handleLegendMouseLeave={handleLegendMouseLeave}
/>
2023-07-10 23:01:22 -07:00
</div>
2023-07-10 04:56:38 -07:00
) : null}
2023-07-06 20:45:32 -07:00
</>
2023-07-10 04:56:38 -07:00
) : null
2023-07-06 20:45:32 -07:00
}
export default HealthContributions