split account page into more components

This commit is contained in:
saml33 2023-07-07 22:34:49 +10:00
parent 9a7304acaa
commit e00af21276
3 changed files with 681 additions and 603 deletions

View File

@ -0,0 +1,501 @@
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useMangoGroup from 'hooks/useMangoGroup'
import { useTranslation } from 'next-i18next'
import { useEffect, useMemo } from 'react'
import { ChartToShow } from './AccountPage'
import { useQuery } from '@tanstack/react-query'
import { fetchFundingTotals, fetchVolumeTotals } from 'utils/account'
import Tooltip from '@components/shared/Tooltip'
import {
HealthType,
toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4'
import HealthBar from './HealthBar'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { IconButton } from '@components/shared/Button'
import { CalendarIcon, ChartBarIcon } from '@heroicons/react/20/solid'
import Change from '@components/shared/Change'
import SheenLoader from '@components/shared/SheenLoader'
import { FormattedHourlyAccountVolumeData, PerformanceDataItem } from 'types'
const AccountHeroStats = ({
accountPnl,
accountValue,
hourlyVolumeData,
loadingHourlyVolume,
rollingDailyData,
setShowPnlHistory,
setChartToShow,
}: {
accountPnl: number
accountValue: number
hourlyVolumeData: FormattedHourlyAccountVolumeData[] | undefined
loadingHourlyVolume: boolean
rollingDailyData: PerformanceDataItem[]
setShowPnlHistory: (show: boolean) => void
setChartToShow: (view: ChartToShow) => void
}) => {
const { t } = useTranslation(['common', 'account'])
const { group } = useMangoGroup()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const totalInterestData = mangoStore(
(s) => s.mangoAccount.interestTotals.data
)
useEffect(() => {
if (mangoAccountAddress) {
const actions = mangoStore.getState().actions
actions.fetchAccountInterestTotals(mangoAccountAddress)
}
}, [mangoAccountAddress])
const {
data: fundingData,
isLoading: loadingFunding,
isFetching: fetchingFunding,
} = useQuery(
['funding', mangoAccountAddress],
() => fetchFundingTotals(mangoAccountAddress),
{
cacheTime: 1000 * 60 * 10,
staleTime: 1000 * 60,
retry: 3,
refetchOnWindowFocus: false,
enabled: !!mangoAccountAddress,
}
)
const {
data: volumeTotalData,
isLoading: loadingVolumeTotalData,
isFetching: fetchingVolumeTotalData,
} = useQuery(
['total-volume', mangoAccountAddress],
() => fetchVolumeTotals(mangoAccountAddress),
{
cacheTime: 1000 * 60 * 10,
staleTime: 1000 * 60,
retry: 3,
refetchOnWindowFocus: false,
enabled: !!mangoAccountAddress,
}
)
const maintHealth = useMemo(() => {
return group && mangoAccount
? mangoAccount.getHealthRatioUi(group, HealthType.maint)
: 0
}, [mangoAccount, group])
const leverage = useMemo(() => {
if (!group || !mangoAccount) return 0
const assetsValue = toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group).toNumber()
)
if (isNaN(assetsValue / accountValue)) {
return 0
} else {
return Math.abs(1 - assetsValue / accountValue)
}
}, [mangoAccount, group, accountValue])
const rollingDailyPnlChange = useMemo(() => {
if (!accountPnl || !rollingDailyData.length) return 0
return accountPnl - rollingDailyData[0].pnl
}, [accountPnl, rollingDailyData])
const interestTotalValue = useMemo(() => {
if (totalInterestData.length) {
return totalInterestData.reduce(
(a, c) => a + (c.borrow_interest_usd * -1 + c.deposit_interest_usd),
0
)
}
return 0.0
}, [totalInterestData])
const fundingTotalValue = useMemo(() => {
if (fundingData?.length && mangoAccountAddress) {
return fundingData.reduce(
(a, c) => a + c.long_funding + c.short_funding,
0
)
}
return 0.0
}, [fundingData, mangoAccountAddress])
const oneDayInterestChange = useMemo(() => {
if (rollingDailyData.length) {
const first = rollingDailyData[0]
const latest = rollingDailyData[rollingDailyData.length - 1]
const startDayInterest =
first.borrow_interest_cumulative_usd +
first.deposit_interest_cumulative_usd
const endDayInterest =
latest.borrow_interest_cumulative_usd +
latest.deposit_interest_cumulative_usd
return endDayInterest - startDayInterest
}
return 0.0
}, [rollingDailyData])
const dailyVolume = useMemo(() => {
if (!hourlyVolumeData || !hourlyVolumeData.length) return 0
// Calculate the current time in milliseconds
const currentTime = new Date().getTime()
// Calculate the start time for the last 24 hours in milliseconds
const last24HoursStartTime = currentTime - 24 * 60 * 60 * 1000
// Filter the formatted data based on the timestamp
const last24HoursData = hourlyVolumeData.filter((entry) => {
const timestampMs = new Date(entry.time).getTime()
return timestampMs >= last24HoursStartTime && timestampMs <= currentTime
})
const volume = last24HoursData.reduce((a, c) => a + c.total_volume_usd, 0)
return volume
}, [hourlyVolumeData])
const handleChartToShow = (
viewName:
| 'pnl'
| 'cumulative-interest-value'
| 'hourly-funding'
| 'hourly-volume'
) => {
setChartToShow(viewName)
}
const loadingTotalVolume = fetchingVolumeTotalData || loadingVolumeTotalData
return (
<>
<div className="grid grid-cols-6 border-b border-th-bkg-3">
<div className="col-span-6 border-t border-th-bkg-3 py-3 px-6 md:col-span-3 lg:col-span-2 lg:border-t-0 xl:col-span-1">
<div id="account-step-four">
<Tooltip
maxWidth="20rem"
placement="top-start"
delay={100}
content={
<div className="flex-col space-y-2 text-sm">
<p className="text-xs">
Health describes how close your account is to liquidation.
The lower your account health is the more likely you are to
get liquidated when prices fluctuate.
</p>
{maintHealth < 100 && mangoAccountAddress ? (
<>
<p className="text-xs font-bold text-th-fgd-1">
Your account health is {maintHealth}%
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
If the prices of all your liabilities increase by{' '}
{maintHealth}%, even for just a moment, some of your
liabilities will be liquidated.
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
If the value of your total collateral decreases by{' '}
{(
(1 - 1 / ((maintHealth || 0) / 100 + 1)) *
100
).toFixed(2)}
% , some of your liabilities will be liquidated.
</p>
<p className="text-xs">
These are examples. A combination of events can also
lead to liquidation.
</p>
</>
) : null}
</div>
}
>
<p className="tooltip-underline text-sm font-normal text-th-fgd-3 xl:text-base">
{t('health')}
</p>
</Tooltip>
<div className="mt-1 mb-0.5 flex items-center space-x-3">
<p className="text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{maintHealth}%
</p>
<HealthBar health={maintHealth} />
</div>
<span className="flex text-xs font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-leverage')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('leverage')}</span>:
</Tooltip>
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue value={leverage} decimals={2} roundUp />x
</span>
</span>
</div>
</div>
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-3 md:border-l lg:col-span-2 lg:border-t-0 xl:col-span-1">
<div id="account-step-five">
<Tooltip
content={t('account:tooltip-free-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('free-collateral')}
</p>
</Tooltip>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group)
)
: 0
}
decimals={2}
isUsd={true}
/>
</p>
<span className="text-xs font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-total-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('total')}</span>:
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group, HealthType.init)
)
: 0
}
decimals={2}
isUsd={true}
/>
</span>
</Tooltip>
</span>
</div>
</div>
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 px-6 md:col-span-3 lg:col-span-2 lg:border-l lg:border-t-0 xl:col-span-1">
<div
id="account-step-seven"
className="flex w-full flex-col items-start"
>
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-pnl')}
placement="top-start"
delay={100}
>
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
{t('pnl')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<div className="flex items-center space-x-3">
<Tooltip
className="hidden md:block"
content={t('account:pnl-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('pnl')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
<Tooltip
className="hidden md:block"
content={t('account:pnl-history')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => setShowPnlHistory(true)}
>
<CalendarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
</div>
) : null}
</div>
<p className="mt-1 mb-0.5 text-left text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={accountPnl}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1.5">
<Change change={rollingDailyPnlChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 md:border-l lg:col-span-2 lg:border-l-0 xl:col-span-1 xl:border-l xl:border-t-0">
<div id="account-step-six">
<div className="flex w-full items-center justify-between">
<p className="text-sm text-th-fgd-3 xl:text-base">
{t('account:lifetime-volume')}
</p>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:volume-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('hourly-volume')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
{loadingTotalVolume && mangoAccountAddress ? (
<SheenLoader className="mt-1">
<div className="h-7 w-16 bg-th-bkg-2" />
</SheenLoader>
) : (
<p className="mt-1 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue value={volumeTotalData || 0} isUsd />
</p>
)}
<span className="flex items-center text-xs font-normal text-th-fgd-4">
<span>{t('account:daily-volume')}</span>:
{loadingHourlyVolume && mangoAccountAddress ? (
<SheenLoader className="ml-1">
<div className="h-3.5 w-10 bg-th-bkg-2" />
</SheenLoader>
) : (
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue
value={dailyVolume}
decimals={2}
isUsd={true}
/>
</span>
)}
</span>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 lg:col-span-2 lg:border-l xl:col-span-1 xl:border-t-0">
<div id="account-step-eight">
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-total-interest')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('total-interest-earned')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:cumulative-interest-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() =>
handleChartToShow('cumulative-interest-value')
}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={interestTotalValue}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1.5">
<Change change={oneDayInterestChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 md:border-l lg:col-span-2 xl:col-span-1 xl:border-t-0">
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-total-funding')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('account:total-funding-earned')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:funding-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('hourly-funding')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
{(loadingFunding || fetchingFunding) && mangoAccountAddress ? (
<SheenLoader className="mt-2">
<div className="h-7 w-16 bg-th-bkg-2" />
</SheenLoader>
) : (
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={fundingTotalValue}
decimals={2}
isUsd={true}
/>
</p>
)}
</div>
</div>
</>
)
}
export default AccountHeroStats

View File

@ -1,54 +1,25 @@
import {
HealthType,
toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4'
import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4'
import { useTranslation } from 'next-i18next'
import { useEffect, useMemo, useState } from 'react'
import { useMemo, useState } from 'react'
import AccountActions from './AccountActions'
import mangoStore from '@store/mangoStore'
import { formatCurrencyValue } from '../../utils/numbers'
import FlipNumbers from 'react-flip-numbers'
import SimpleAreaChart from '@components/shared/SimpleAreaChart'
import { COLORS } from '../../styles/colors'
import { useTheme } from 'next-themes'
import { IconButton } from '../shared/Button'
import {
ArrowsPointingOutIcon,
CalendarIcon,
ChartBarIcon,
} from '@heroicons/react/20/solid'
import { Transition } from '@headlessui/react'
import AccountTabs from './AccountTabs'
import SheenLoader from '../shared/SheenLoader'
import AccountChart from './AccountChart'
import useMangoAccount from '../../hooks/useMangoAccount'
import Change from '../shared/Change'
import Tooltip from '@components/shared/Tooltip'
import {
ANIMATION_SETTINGS_KEY,
// IS_ONBOARDED_KEY
} from 'utils/constants'
import useLocalStorageState from 'hooks/useLocalStorageState'
// import AccountOnboardingTour from '@components/tours/AccountOnboardingTour'
import dayjs from 'dayjs'
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
import useMangoGroup from 'hooks/useMangoGroup'
import PnlHistoryModal from '@components/modals/PnlHistoryModal'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import HealthBar from './HealthBar'
import AssetsLiabilities from './AssetsLiabilities'
import { PerformanceDataItem } from 'types'
import { useQuery } from '@tanstack/react-query'
import FundingChart from './FundingChart'
import VolumeChart from './VolumeChart'
import {
fetchAccountPerformance,
fetchFundingTotals,
fetchHourlyVolume,
fetchVolumeTotals,
} from 'utils/account'
import { fetchAccountPerformance, fetchHourlyVolume } from 'utils/account'
import AccountHeroStats from './AccountHeroStats'
import AccountValue from './AccountValue'
const TABS = ['account-value', 'account:assets-liabilities']
@ -64,34 +35,17 @@ const AccountPage = () => {
const { t } = useTranslation(['common', 'account'])
const { group } = useMangoGroup()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const totalInterestData = mangoStore(
(s) => s.mangoAccount.interestTotals.data
)
const [chartToShow, setChartToShow] = useState<ChartToShow>('')
const [showExpandChart, setShowExpandChart] = useState<boolean>(false)
const [showPnlHistory, setShowPnlHistory] = useState<boolean>(false)
const { theme } = useTheme()
const { width } = useViewport()
const isMobile = width ? width < breakpoints.md : false
// const tourSettings = mangoStore((s) => s.settings.tours)
// const [isOnBoarded] = useLocalStorageState(IS_ONBOARDED_KEY)
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
const [activeTab, setActiveTab] = useLocalStorageState(
'accountHeroKey-0.1',
'account-value'
)
useEffect(() => {
if (mangoAccountAddress) {
const actions = mangoStore.getState().actions
actions.fetchAccountInterestTotals(mangoAccountAddress)
}
}, [mangoAccountAddress])
const {
data: performanceData,
isLoading: loadingPerformanceData,
@ -108,38 +62,6 @@ const AccountPage = () => {
}
)
const {
data: fundingData,
isLoading: loadingFunding,
isFetching: fetchingFunding,
} = useQuery(
['funding', mangoAccountAddress],
() => fetchFundingTotals(mangoAccountAddress),
{
cacheTime: 1000 * 60 * 10,
staleTime: 1000 * 60,
retry: 3,
refetchOnWindowFocus: false,
enabled: !!mangoAccountAddress,
}
)
const {
data: volumeTotalData,
isLoading: loadingVolumeTotalData,
isFetching: fetchingVolumeTotalData,
} = useQuery(
['total-volume', mangoAccountAddress],
() => fetchVolumeTotals(mangoAccountAddress),
{
cacheTime: 1000 * 60 * 10,
staleTime: 1000 * 60,
retry: 3,
refetchOnWindowFocus: false,
enabled: !!mangoAccountAddress,
}
)
const {
data: hourlyVolumeData,
isLoading: loadingHourlyVolumeData,
@ -156,25 +78,7 @@ const AccountPage = () => {
}
)
const dailyVolume = useMemo(() => {
if (!hourlyVolumeData || !hourlyVolumeData.length) return 0
// Calculate the current time in milliseconds
const currentTime = new Date().getTime()
// Calculate the start time for the last 24 hours in milliseconds
const last24HoursStartTime = currentTime - 24 * 60 * 60 * 1000
// Filter the formatted data based on the timestamp
const last24HoursData = hourlyVolumeData.filter((entry) => {
const timestampMs = new Date(entry.time).getTime()
return timestampMs >= last24HoursStartTime && timestampMs <= currentTime
})
const volume = last24HoursData.reduce((a, c) => a + c.total_volume_usd, 0)
return volume
}, [hourlyVolumeData])
const oneDayPerformanceData: PerformanceDataItem[] | [] = useMemo(() => {
const rollingDailyData: PerformanceDataItem[] | [] = useMemo(() => {
if (!performanceData || !performanceData.length) return []
const nowDate = new Date()
return performanceData.filter((d) => {
@ -183,20 +87,6 @@ const AccountPage = () => {
})
}, [performanceData])
const onHoverMenu = (open: boolean, action: string) => {
if (
(!open && action === 'onMouseEnter') ||
(open && action === 'onMouseLeave')
) {
setShowExpandChart(!open)
}
}
const handleShowAccountValueChart = () => {
setChartToShow('account-value')
setShowExpandChart(false)
}
const handleHideChart = () => {
setChartToShow('')
}
@ -213,89 +103,17 @@ const AccountPage = () => {
]
}, [group, mangoAccount])
const leverage = useMemo(() => {
if (!group || !mangoAccount) return 0
const assetsValue = toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group).toNumber()
)
const pnlChangeToday = useMemo(() => {
if (!accountPnl || !rollingDailyData.length) return 0
const startHour = rollingDailyData.find((item) => {
const itemHour = new Date(item.time).getHours()
return itemHour === 0
})
const startDayPnl = startHour?.pnl
const pnlChangeToday = startDayPnl ? accountPnl - startDayPnl : 0
if (isNaN(assetsValue / accountValue)) {
return 0
} else {
return Math.abs(1 - assetsValue / accountValue)
}
}, [mangoAccount, group, accountValue])
const [accountValueChange, rollingDailyPnlChange, pnlChangeToday] =
useMemo(() => {
if (!accountValue || !accountPnl || !oneDayPerformanceData.length)
return [0, 0, 0]
const accountValueChange =
accountValue - oneDayPerformanceData[0].account_equity
const startHour = oneDayPerformanceData.find((item) => {
const itemHour = new Date(item.time).getHours()
return itemHour === 0
})
const startDayPnl = startHour?.pnl
const rollingDailyPnlChange = accountPnl - oneDayPerformanceData[0].pnl
const pnlChangeToday = startDayPnl ? accountPnl - startDayPnl : 0
return [accountValueChange, rollingDailyPnlChange, pnlChangeToday]
}, [accountPnl, accountValue, oneDayPerformanceData])
const interestTotalValue = useMemo(() => {
if (totalInterestData.length) {
return totalInterestData.reduce(
(a, c) => a + (c.borrow_interest_usd * -1 + c.deposit_interest_usd),
0
)
}
return 0.0
}, [totalInterestData])
const fundingTotalValue = useMemo(() => {
if (fundingData?.length && mangoAccountAddress) {
return fundingData.reduce(
(a, c) => a + c.long_funding + c.short_funding,
0
)
}
return 0.0
}, [fundingData, mangoAccountAddress])
const oneDayInterestChange = useMemo(() => {
if (oneDayPerformanceData.length) {
const first = oneDayPerformanceData[0]
const latest = oneDayPerformanceData[oneDayPerformanceData.length - 1]
const startDayInterest =
first.borrow_interest_cumulative_usd +
first.deposit_interest_cumulative_usd
const endDayInterest =
latest.borrow_interest_cumulative_usd +
latest.deposit_interest_cumulative_usd
return endDayInterest - startDayInterest
}
return 0.0
}, [oneDayPerformanceData])
const maintHealth = useMemo(() => {
return group && mangoAccount
? mangoAccount.getHealthRatioUi(group, HealthType.maint)
: 0
}, [mangoAccount, group])
const handleChartToShow = (
chartName:
| 'pnl'
| 'cumulative-interest-value'
| 'hourly-funding'
| 'hourly-volume'
) => {
setChartToShow(chartName)
}
return pnlChangeToday
}, [accountPnl, rollingDailyData])
const latestAccountData = useMemo(() => {
if (!accountValue || !performanceData || !performanceData.length) return []
@ -315,7 +133,6 @@ const AccountPage = () => {
]
}, [accountPnl, accountValue, performanceData])
const loadingTotalVolume = fetchingVolumeTotalData || loadingVolumeTotalData
const loadingHourlyVolume =
fetchingHourlyVolumeData || loadingHourlyVolumeData
@ -342,93 +159,13 @@ const AccountPage = () => {
</div>
<div className="md:h-24">
{activeTab === 'account-value' ? (
<div className="flex flex-col md:flex-row md:items-end md:space-x-6">
<div className="mx-auto mt-4 md:mx-0">
<div className="mb-2 flex justify-start font-display text-5xl text-th-fgd-1">
{animationSettings['number-scroll'] ? (
group && mangoAccount ? (
<FlipNumbers
height={48}
width={35}
play
delay={0.05}
duration={1}
numbers={formatCurrencyValue(accountValue, 2)}
/>
) : (
<FlipNumbers
height={48}
width={36}
play
delay={0.05}
duration={1}
numbers={'$0.00'}
/>
)
) : (
<FormatNumericValue
value={accountValue}
isUsd
decimals={2}
/>
)}
</div>
<div className="flex items-center justify-center space-x-1.5 md:justify-start">
<Change change={accountValueChange} prefix="$" />
<p className="text-xs text-th-fgd-4">
{t('rolling-change')}
</p>
</div>
</div>
{!performanceLoading ? (
oneDayPerformanceData.length ? (
<div
className="relative mt-4 flex h-40 items-end md:mt-0 md:h-20 md:w-52 lg:w-60"
onMouseEnter={() =>
onHoverMenu(showExpandChart, 'onMouseEnter')
}
onMouseLeave={() =>
onHoverMenu(showExpandChart, 'onMouseLeave')
}
>
<SimpleAreaChart
color={
accountValueChange >= 0
? COLORS.UP[theme]
: COLORS.DOWN[theme]
}
data={oneDayPerformanceData.concat(latestAccountData)}
name="accountValue"
xKey="time"
yKey="account_equity"
/>
<Transition
appear={true}
className="absolute right-2 bottom-2"
show={showExpandChart || isMobile}
enter="transition ease-in duration-300"
enterFrom="opacity-0 scale-75"
enterTo="opacity-100 scale-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleShowAccountValueChart()}
>
<ArrowsPointingOutIcon className="h-5 w-5" />
</IconButton>
</Transition>
</div>
) : null
) : mangoAccountAddress ? (
<SheenLoader className="mt-4 flex flex-1 md:mt-0">
<div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-20 md:w-52 lg:w-60" />
</SheenLoader>
) : null}
</div>
<AccountValue
accountValue={accountValue}
latestAccountData={latestAccountData}
loading={performanceLoading}
rollingDailyData={rollingDailyData}
setChartToShow={setChartToShow}
/>
) : null}
{activeTab === 'account:assets-liabilities' ? (
<AssetsLiabilities isMobile={isMobile} />
@ -439,323 +176,15 @@ const AccountPage = () => {
<AccountActions />
</div>
</div>
<div className="grid grid-cols-6 border-b border-th-bkg-3">
<div className="col-span-6 border-t border-th-bkg-3 py-3 px-6 md:col-span-3 lg:col-span-2 lg:border-t-0 xl:col-span-1">
<div id="account-step-four">
<Tooltip
maxWidth="20rem"
placement="top-start"
delay={100}
content={
<div className="flex-col space-y-2 text-sm">
<p className="text-xs">
Health describes how close your account is to liquidation.
The lower your account health is the more likely you are to
get liquidated when prices fluctuate.
</p>
{maintHealth < 100 && mangoAccountAddress ? (
<>
<p className="text-xs font-bold text-th-fgd-1">
Your account health is {maintHealth}%
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
If the prices of all your liabilities increase by{' '}
{maintHealth}%, even for just a moment, some of your
liabilities will be liquidated.
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
If the value of your total collateral decreases by{' '}
{(
(1 - 1 / ((maintHealth || 0) / 100 + 1)) *
100
).toFixed(2)}
% , some of your liabilities will be liquidated.
</p>
<p className="text-xs">
These are examples. A combination of events can also
lead to liquidation.
</p>
</>
) : null}
</div>
}
>
<p className="tooltip-underline text-sm font-normal text-th-fgd-3 xl:text-base">
{t('health')}
</p>
</Tooltip>
<div className="mt-1 mb-0.5 flex items-center space-x-3">
<p className="text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{maintHealth}%
</p>
<HealthBar health={maintHealth} />
</div>
<span className="flex text-xs font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-leverage')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('leverage')}</span>:
</Tooltip>
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue value={leverage} decimals={2} roundUp />x
</span>
</span>
</div>
</div>
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-3 md:border-l lg:col-span-2 lg:border-t-0 xl:col-span-1">
<div id="account-step-five">
<Tooltip
content={t('account:tooltip-free-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('free-collateral')}
</p>
</Tooltip>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group)
)
: 0
}
decimals={2}
isUsd={true}
/>
</p>
<span className="text-xs font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-total-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('total')}</span>:
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group, HealthType.init)
)
: 0
}
decimals={2}
isUsd={true}
/>
</span>
</Tooltip>
</span>
</div>
</div>
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 px-6 md:col-span-3 lg:col-span-2 lg:border-l lg:border-t-0 xl:col-span-1">
<div
id="account-step-seven"
className="flex w-full flex-col items-start"
>
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-pnl')}
placement="top-start"
delay={100}
>
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
{t('pnl')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<div className="flex items-center space-x-3">
<Tooltip
className="hidden md:block"
content={t('account:pnl-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('pnl')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
<Tooltip
className="hidden md:block"
content={t('account:pnl-history')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => setShowPnlHistory(true)}
>
<CalendarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
</div>
) : null}
</div>
<p className="mt-1 mb-0.5 text-left text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={accountPnl}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1.5">
<Change change={rollingDailyPnlChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 md:border-l lg:col-span-2 lg:border-l-0 xl:col-span-1 xl:border-l xl:border-t-0">
<div id="account-step-six">
<div className="flex w-full items-center justify-between">
<p className="text-sm text-th-fgd-3 xl:text-base">
{t('account:lifetime-volume')}
</p>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:volume-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('hourly-volume')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
{loadingTotalVolume && mangoAccountAddress ? (
<SheenLoader className="mt-1">
<div className="h-7 w-16 bg-th-bkg-2" />
</SheenLoader>
) : (
<p className="mt-1 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue value={volumeTotalData || 0} isUsd />
</p>
)}
<span className="flex items-center text-xs font-normal text-th-fgd-4">
<span>{t('account:daily-volume')}</span>:
{loadingHourlyVolume && mangoAccountAddress ? (
<SheenLoader className="ml-1">
<div className="h-3.5 w-10 bg-th-bkg-2" />
</SheenLoader>
) : (
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue
value={dailyVolume}
decimals={2}
isUsd={true}
/>
</span>
)}
</span>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 lg:col-span-2 lg:border-l xl:col-span-1 xl:border-t-0">
<div id="account-step-eight">
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-total-interest')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('total-interest-earned')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:cumulative-interest-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() =>
handleChartToShow('cumulative-interest-value')
}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={interestTotalValue}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1.5">
<Change change={oneDayInterestChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
</div>
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 md:border-l lg:col-span-2 xl:col-span-1 xl:border-t-0">
<div className="flex w-full items-center justify-between">
<Tooltip
content={t('account:tooltip-total-funding')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
{t('account:total-funding-earned')}
</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:funding-chart')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleChartToShow('hourly-funding')}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
{(loadingFunding || fetchingFunding) && mangoAccountAddress ? (
<SheenLoader className="mt-2">
<div className="h-7 w-16 bg-th-bkg-2" />
</SheenLoader>
) : (
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
<FormatNumericValue
value={fundingTotalValue}
decimals={2}
isUsd={true}
/>
</p>
)}
</div>
</div>
<AccountHeroStats
accountPnl={accountPnl}
accountValue={accountValue}
hourlyVolumeData={hourlyVolumeData}
loadingHourlyVolume={loadingHourlyVolume}
rollingDailyData={rollingDailyData}
setChartToShow={setChartToShow}
setShowPnlHistory={setShowPnlHistory}
/>
<AccountTabs />
{/* {!tourSettings?.account_tour_seen && isOnBoarded && connected ? (
<AccountOnboardingTour />

View File

@ -0,0 +1,148 @@
import { formatCurrencyValue } from '../../utils/numbers'
import FlipNumbers from 'react-flip-numbers'
import SimpleAreaChart from '@components/shared/SimpleAreaChart'
import { COLORS } from '../../styles/colors'
import { IconButton } from '../shared/Button'
import { ArrowsPointingOutIcon } from '@heroicons/react/20/solid'
import { Transition } from '@headlessui/react'
import SheenLoader from '../shared/SheenLoader'
import Change from '../shared/Change'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { useTheme } from 'next-themes'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
import useMangoGroup from 'hooks/useMangoGroup'
import useMangoAccount from 'hooks/useMangoAccount'
import { PerformanceDataItem } from 'types'
import { useMemo, useState } from 'react'
import { useTranslation } from 'next-i18next'
import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
import { ChartToShow } from './AccountPage'
const AccountValue = ({
accountValue,
latestAccountData,
loading,
rollingDailyData,
setChartToShow,
}: {
accountValue: number
latestAccountData: PerformanceDataItem[]
loading: boolean
rollingDailyData: PerformanceDataItem[]
setChartToShow: (chart: ChartToShow) => void
}) => {
const { t } = useTranslation('common')
const { theme } = useTheme()
const { group } = useMangoGroup()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const [showExpandChart, setShowExpandChart] = useState<boolean>(false)
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
const { width } = useViewport()
const isMobile = width ? width < breakpoints.md : false
const accountValueChange = useMemo(() => {
if (!accountValue || !rollingDailyData.length) return 0
const accountValueChange = accountValue - rollingDailyData[0].account_equity
return accountValueChange
}, [accountValue, rollingDailyData])
const onHoverMenu = (open: boolean, action: string) => {
if (
(!open && action === 'onMouseEnter') ||
(open && action === 'onMouseLeave')
) {
setShowExpandChart(!open)
}
}
const handleShowAccountValueChart = () => {
setChartToShow('account-value')
setShowExpandChart(false)
}
return (
<div className="flex flex-col md:flex-row md:items-end md:space-x-6">
<div className="mx-auto mt-4 md:mx-0">
<div className="mb-2 flex justify-start font-display text-5xl text-th-fgd-1">
{animationSettings['number-scroll'] ? (
group && mangoAccount ? (
<FlipNumbers
height={48}
width={35}
play
delay={0.05}
duration={1}
numbers={formatCurrencyValue(accountValue, 2)}
/>
) : (
<FlipNumbers
height={48}
width={36}
play
delay={0.05}
duration={1}
numbers={'$0.00'}
/>
)
) : (
<FormatNumericValue value={accountValue} isUsd decimals={2} />
)}
</div>
<div className="flex items-center justify-center space-x-1.5 md:justify-start">
<Change change={accountValueChange} prefix="$" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
{!loading ? (
rollingDailyData.length ? (
<div
className="relative mt-4 flex h-40 items-end md:mt-0 md:h-20 md:w-52 lg:w-60"
onMouseEnter={() => onHoverMenu(showExpandChart, 'onMouseEnter')}
onMouseLeave={() => onHoverMenu(showExpandChart, 'onMouseLeave')}
>
<SimpleAreaChart
color={
accountValueChange >= 0 ? COLORS.UP[theme] : COLORS.DOWN[theme]
}
data={rollingDailyData.concat(latestAccountData)}
name="accountValue"
xKey="time"
yKey="account_equity"
/>
<Transition
appear={true}
className="absolute right-2 bottom-2"
show={showExpandChart || isMobile}
enter="transition ease-in duration-300"
enterFrom="opacity-0 scale-75"
enterTo="opacity-100 scale-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleShowAccountValueChart()}
>
<ArrowsPointingOutIcon className="h-5 w-5" />
</IconButton>
</Transition>
</div>
) : null
) : mangoAccountAddress ? (
<SheenLoader className="mt-4 flex flex-1 md:mt-0">
<div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-20 md:w-52 lg:w-60" />
</SheenLoader>
) : null}
</div>
)
}
export default AccountValue