mango-v4-ui/components/token/CoingeckoStats.tsx

313 lines
11 KiB
TypeScript
Raw Normal View History

2022-12-06 16:22:32 -08:00
import { Bank } from '@blockworks-foundation/mango-v4'
import Change from '@components/shared/Change'
import ChartRangeButtons from '@components/shared/ChartRangeButtons'
2023-01-24 16:54:24 -08:00
import FormatNumericValue from '@components/shared/FormatNumericValue'
import SheenLoader from '@components/shared/SheenLoader'
2022-12-11 15:21:40 -08:00
import { ArrowSmallUpIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
import { useQuery } from '@tanstack/react-query'
import { makeApiRequest } from 'apis/birdeye/helpers'
2022-12-06 16:22:32 -08:00
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { BirdeyePriceResponse } from 'hooks/useBirdeyeMarketPrices'
2022-12-06 16:22:32 -08:00
import parse from 'html-react-parser'
import { useTranslation } from 'next-i18next'
import { useMemo, useState } from 'react'
2023-05-01 19:01:14 -07:00
import PriceChart from '@components/token/PriceChart'
2022-12-06 16:22:32 -08:00
dayjs.extend(relativeTime)
const DEFAULT_COINGECKO_VALUES = {
ath: 0,
atl: 0,
ath_change_percentage: 0,
atl_change_percentage: 0,
ath_date: 0,
atl_date: 0,
high_24h: 0,
circulating_supply: 0,
fully_diluted_valuation: 0,
low_24h: 0,
market_cap: 0,
max_supply: 0,
price_change_percentage_24h: 0,
total_supply: 0,
total_volume: 0,
}
interface BirdeyeResponse {
data: { items: BirdeyePriceResponse[] }
success: boolean
}
const fetchBirdeyePrices = async (
daysToShow: string,
mint: string
): Promise<BirdeyePriceResponse[] | []> => {
const interval = daysToShow === '1' ? '30m' : daysToShow === '7' ? '1H' : '4H'
const queryEnd = Math.floor(Date.now() / 1000)
const queryStart = queryEnd - parseInt(daysToShow) * 86400
const query = `defi/history_price?address=${mint}&address_type=token&type=${interval}&time_from=${queryStart}&time_to=${queryEnd}`
const response: BirdeyeResponse = await makeApiRequest(query)
if (response.success && response?.data?.items) {
return response.data.items
}
return []
}
2022-12-06 16:22:32 -08:00
const CoingeckoStats = ({
bank,
coingeckoData,
}: {
bank: Bank
2023-02-27 23:20:11 -08:00
// TODO: Add Coingecko api types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2022-12-06 16:22:32 -08:00
coingeckoData: any
}) => {
const { t } = useTranslation(['common', 'token'])
const [showFullDesc, setShowFullDesc] = useState(false)
const [daysToShow, setDaysToShow] = useState<string>('1')
const {
data: birdeyePrices,
isLoading: loadingBirdeyePrices,
isFetching: fetchingBirdeyePrices,
} = useQuery(
2023-04-12 17:31:22 -07:00
['birdeye-token-prices', daysToShow, bank.mint],
() => fetchBirdeyePrices(daysToShow, bank.mint.toString()),
{
cacheTime: 1000 * 60 * 15,
staleTime: 1000 * 60 * 10,
retry: 3,
enabled: !!bank,
refetchOnWindowFocus: false,
2022-12-06 16:22:32 -08:00
}
)
2022-12-06 16:22:32 -08:00
const {
ath,
atl,
ath_change_percentage,
atl_change_percentage,
ath_date,
atl_date,
circulating_supply,
fully_diluted_valuation,
market_cap,
max_supply,
total_supply,
total_volume,
} = coingeckoData ? coingeckoData : DEFAULT_COINGECKO_VALUES
2022-12-06 16:22:32 -08:00
const truncateDescription = (desc: string) =>
desc.substring(0, (desc + ' ').lastIndexOf(' ', 144))
const description = useMemo(() => {
const desc = coingeckoData?.description?.en
if (!desc) return ''
return showFullDesc
? coingeckoData.description.en
: truncateDescription(coingeckoData.description.en)
}, [coingeckoData, showFullDesc])
2022-12-06 16:22:32 -08:00
return (
<>
{description ? (
2023-01-11 04:25:38 -08:00
<div className="border-b border-th-bkg-3 py-4 px-6">
<h2 className="mb-1 text-xl">About {bank.name}</h2>
<div className="flex items-end">
<p className="max-w-[720px]">{parse(description)}</p>
{coingeckoData.description.en.length > description.length ||
showFullDesc ? (
2023-01-11 04:25:38 -08:00
<span
2023-04-19 18:12:45 -07:00
className="ml-4 flex cursor-pointer items-end font-normal underline hover:text-th-fgd-2 md:hover:no-underline"
2023-01-11 04:25:38 -08:00
onClick={() => setShowFullDesc(!showFullDesc)}
>
{showFullDesc ? 'Less' : 'More'}
<ArrowSmallUpIcon
className={`h-5 w-5 ${
showFullDesc ? 'rotate-360' : 'rotate-180'
} default-transition`}
/>
</span>
) : null}
</div>
2022-12-06 16:22:32 -08:00
</div>
2023-01-11 04:25:38 -08:00
) : null}
<div className="mt-4 flex w-full items-center justify-between px-6">
<h2 className="text-base">{bank.name} Price Chart</h2>
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
values={['1', '7', '30']}
onChange={(v) => setDaysToShow(v)}
/>
</div>
{birdeyePrices?.length ? (
<PriceChart daysToShow={parseInt(daysToShow)} prices={birdeyePrices} />
) : loadingBirdeyePrices || fetchingBirdeyePrices ? (
<div className="p-6">
<SheenLoader className="flex flex-1">
<div className="h-72 w-full rounded-lg bg-th-bkg-2 md:h-80" />
</SheenLoader>
</div>
) : (
<div className="m-6 flex h-72 items-center justify-center rounded-lg border border-th-bkg-3 md:h-80">
<div className="flex flex-col items-center">
<NoSymbolIcon className="mb-2 h-7 w-7 text-th-fgd-4" />
<p>{t('chart-unavailable')}</p>
</div>
</div>
2022-12-06 16:22:32 -08:00
)}
<div className="grid grid-cols-1 border-b border-th-bkg-3 md:grid-cols-2">
<div className="col-span-1 border-y border-th-bkg-3 px-6 py-4 md:col-span-2">
<h2 className="text-base">{bank.name} Stats</h2>
</div>
2023-04-30 20:10:24 -07:00
<div className="col-span-1 px-6 py-4 md:border-r md:border-th-bkg-3">
2022-12-06 16:22:32 -08:00
<div className="flex justify-between pb-4">
<p>{t('token:market-cap')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{market_cap?.usd ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={market_cap.usd} isUsd />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}{' '}
2022-12-06 16:22:32 -08:00
<span className="text-th-fgd-4">
2022-12-11 15:03:14 -08:00
{coingeckoData.market_cap_rank
? `#${coingeckoData.market_cap_rank}`
: ''}
2022-12-06 16:22:32 -08:00
</span>
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('token:volume')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{total_volume?.usd ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={total_volume.usd} isUsd />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('token:all-time-high')}</p>
<div className="flex flex-col items-end">
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
2022-12-11 15:03:14 -08:00
{ath?.usd ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={ath.usd} isUsd />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</span>
2022-12-11 15:03:14 -08:00
{ath_change_percentage.usd ? (
<Change change={ath_change_percentage.usd} suffix="%" />
) : null}
2022-12-06 16:22:32 -08:00
</div>
<p className="text-xs text-th-fgd-4">
{dayjs(ath_date.usd).format('MMM, D, YYYY')} (
{dayjs(ath_date.usd).fromNow()})
</p>
</div>
</div>
<div className="flex justify-between border-b border-t border-th-bkg-3 py-4 md:border-b-0 md:pb-0">
<p>{t('token:all-time-low')}</p>
<div className="flex flex-col items-end">
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
2022-12-11 15:03:14 -08:00
{atl?.usd ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={atl.usd} isUsd />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</span>
<Change change={atl_change_percentage.usd} suffix="%" />
</div>
<p className="text-xs text-th-fgd-4">
{dayjs(atl_date.usd).format('MMM, D, YYYY')} (
{dayjs(atl_date.usd).fromNow()})
</p>
</div>
</div>
</div>
<div className="col-span-1 px-6 pb-4 md:pt-4">
{fully_diluted_valuation.usd ? (
<div className="flex justify-between pb-4">
<p>{t('token:fdv')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{fully_diluted_valuation?.usd ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue
value={fully_diluted_valuation.usd}
isUsd
/>
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</p>
</div>
) : null}
<div
className={`flex justify-between ${
fully_diluted_valuation.usd
? 'border-t border-th-bkg-3 py-4'
: 'pb-4'
}`}
>
<p>{t('token:circulating-supply')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{circulating_supply ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={circulating_supply} />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</p>
</div>
<div
className={`flex justify-between border-t border-th-bkg-3 ${
max_supply ? 'py-4' : 'border-b pt-4 md:pb-4'
}`}
>
<p>{t('token:total-supply')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{total_supply ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={total_supply} />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</p>
</div>
{max_supply ? (
<div className="flex justify-between border-t border-th-bkg-3 pt-4">
<p>{t('token:max-supply')}</p>
<p className="font-mono text-th-fgd-2">
2022-12-11 15:03:14 -08:00
{max_supply ? (
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={max_supply} />
2022-12-11 15:03:14 -08:00
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
</span>
)}
2022-12-06 16:22:32 -08:00
</p>
</div>
) : null}
</div>
</div>
</>
)
}
export default CoingeckoStats