mango-v4-ui/components/swap/SwapTokenChart.tsx

291 lines
9.6 KiB
TypeScript
Raw Normal View History

2022-11-05 04:47:54 -07:00
import { FunctionComponent, useMemo, useState } from 'react'
2022-07-10 19:01:16 -07:00
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
2022-07-12 20:58:13 -07:00
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
2022-09-14 12:38:43 -07:00
Text,
2022-07-12 20:58:13 -07:00
} from 'recharts'
2022-07-22 10:07:55 -07:00
import FlipNumbers from 'react-flip-numbers'
2022-07-10 19:01:16 -07:00
import LineChartIcon from '../icons/LineChartIcon'
import ContentBox from '../shared/ContentBox'
2022-07-22 14:39:02 -07:00
import { formatFixedDecimals } from '../../utils/numbers'
2022-07-23 21:27:54 -07:00
import SheenLoader from '../shared/SheenLoader'
2022-08-02 17:17:42 -07:00
import { COLORS } from '../../styles/colors'
import { useTheme } from 'next-themes'
2022-10-05 22:11:28 -07:00
import Change from '../shared/Change'
2022-09-07 23:25:32 -07:00
import ChartRangeButtons from '../shared/ChartRangeButtons'
2022-09-14 12:38:43 -07:00
import { useViewport } from 'hooks/useViewport'
2022-10-05 19:23:31 -07:00
import { formatTokenSymbol } from 'utils/tokens'
2022-10-07 16:39:06 -07:00
import { useQuery } from '@tanstack/react-query'
2022-10-10 10:28:53 -07:00
import mangoStore from '@store/mangoStore'
2022-11-05 04:47:54 -07:00
import { fetchChartData, USDC_MINT, USDT_MINT } from 'apis/birdeye'
2022-07-10 19:01:16 -07:00
dayjs.extend(relativeTime)
interface SwapTokenChartProps {
2022-11-05 04:47:54 -07:00
inputMint: string
outputMint: string
}
2022-09-14 12:38:43 -07:00
const CustomizedLabel = ({
chartData,
x,
y,
value,
}: {
chartData: any[]
x?: number
y?: string | number
value?: number
}) => {
const { width } = useViewport()
const { theme } = useTheme()
const [min, max] = useMemo(() => {
if (chartData.length) {
const prices = chartData.map((d: any) => d.price)
return [Math.min(...prices), Math.max(...prices)]
}
return ['', '']
}, [chartData])
if (value === min || value === max) {
return (
<Text
x={x}
y={y}
dy={value === min ? 16 : -8}
fill={theme === 'Light' ? 'rgba(0,0,0,0.6)' : 'rgba(255,255,255,0.7)'}
fontSize={10}
textAnchor={x && y && x > width / 3 ? 'end' : 'start'}
2022-09-22 22:09:38 -07:00
className="font-mono"
2022-09-14 12:38:43 -07:00
>
{formatFixedDecimals(value)}
</Text>
)
} else return <div />
}
2022-07-10 19:01:16 -07:00
const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
2022-11-05 04:47:54 -07:00
inputMint,
outputMint,
2022-07-10 19:01:16 -07:00
}) => {
2022-10-10 10:28:53 -07:00
const inputBank = mangoStore((s) => s.swap.inputBank)
const outputBank = mangoStore((s) => s.swap.outputBank)
2022-07-10 19:01:16 -07:00
const [mouseData, setMouseData] = useState<any>(null)
const [daysToShow, setDaysToShow] = useState(1)
2022-08-02 17:17:42 -07:00
const { theme } = useTheme()
2022-11-05 04:47:54 -07:00
2022-10-07 16:39:06 -07:00
const chartDataQuery = useQuery(
2022-11-05 04:47:54 -07:00
['chart-data', inputMint, outputMint, daysToShow],
() => fetchChartData(inputMint, outputMint, daysToShow),
2022-10-07 16:39:06 -07:00
{ staleTime: 120000 }
)
const chartData = chartDataQuery.data
2022-07-10 19:01:16 -07:00
const handleMouseMove = (coords: any) => {
if (coords.activePayload) {
setMouseData(coords.activePayload[0].payload)
}
}
const handleMouseLeave = () => {
setMouseData(null)
}
2022-07-19 20:49:33 -07:00
const calculateChartChange = () => {
if (chartData.length) {
2022-07-26 04:15:07 -07:00
if (mouseData) {
const index = chartData.findIndex((d: any) => d.time === mouseData.time)
return (
2022-07-29 10:57:12 -07:00
((chartData[index]['price'] - chartData[0]['price']) /
2022-07-26 04:15:07 -07:00
chartData[0]['price']) *
100
)
} else
return (
((chartData[chartData.length - 1]['price'] - chartData[0]['price']) /
chartData[0]['price']) *
100
)
2022-07-19 20:49:33 -07:00
}
return 0
}
2022-07-10 19:01:16 -07:00
return (
2022-10-05 19:23:31 -07:00
<ContentBox hideBorder hidePadding className="h-full px-6 py-3">
2022-10-07 16:39:06 -07:00
{chartDataQuery?.isLoading ? (
<>
<SheenLoader className="w-[148px] rounded-md">
<div className="h-[18px] bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="mt-2 w-[148px] rounded-md">
<div className="h-[48px] bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="mt-2 w-[148px] rounded-md">
<div className="h-[18px] bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="mt-4 w-full rounded-md">
2022-10-30 04:19:04 -07:00
<div className="h-[308px] bg-th-bkg-2" />
</SheenLoader>
</>
2022-11-05 04:47:54 -07:00
) : chartData.length && inputMint && outputMint ? (
2022-09-19 18:09:34 -07:00
<div className="relative">
2022-07-10 19:01:16 -07:00
<div className="flex items-start justify-between">
<div>
2022-10-10 10:28:53 -07:00
{inputBank && outputBank ? (
2022-08-18 06:59:36 -07:00
<div className="mb-0.5 flex items-center">
<p className="text-base text-th-fgd-3">
2022-11-05 04:47:54 -07:00
{[USDC_MINT, USDT_MINT].includes(inputMint || '')
2022-10-05 19:23:31 -07:00
? `${formatTokenSymbol(
2022-10-10 10:28:53 -07:00
outputBank?.name?.toUpperCase()
)}/${inputBank?.name?.toUpperCase()}`
2022-10-05 19:23:31 -07:00
: `${formatTokenSymbol(
2022-10-10 10:28:53 -07:00
inputBank?.name?.toUpperCase()
2022-10-05 19:23:31 -07:00
)}/${formatTokenSymbol(
2022-10-10 10:28:53 -07:00
outputBank?.name?.toUpperCase()
2022-10-05 19:23:31 -07:00
)}`}
2022-08-18 06:59:36 -07:00
</p>
</div>
2022-07-10 19:01:16 -07:00
) : null}
{mouseData ? (
<>
2022-11-05 04:47:54 -07:00
<div className="mb-1 flex flex-col text-5xl font-bold text-th-fgd-1 md:flex-row md:items-end">
2022-07-22 10:07:55 -07:00
<FlipNumbers
2022-09-22 22:09:38 -07:00
height={48}
width={32}
2022-07-22 10:07:55 -07:00
play
2022-07-22 14:39:02 -07:00
numbers={formatFixedDecimals(mouseData['price'])}
2022-07-22 10:07:55 -07:00
/>
2022-07-10 19:01:16 -07:00
<span
2022-09-07 18:52:47 -07:00
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}
2022-07-10 19:01:16 -07:00
>
2022-10-05 22:11:28 -07:00
<Change change={calculateChartChange()} />
2022-07-10 19:01:16 -07:00
</span>
</div>
2022-07-24 18:47:12 -07:00
<p className="text-sm text-th-fgd-4">
2022-11-05 04:47:54 -07:00
{dayjs(mouseData.time * 1000).format('DD MMM YY, h:mma')}
2022-07-15 05:50:29 -07:00
</p>
2022-07-10 19:01:16 -07:00
</>
) : (
<>
2022-09-22 22:09:38 -07:00
<div className="mb-1 flex flex-col text-5xl font-bold text-th-fgd-1 md:flex-row md:items-end">
2022-07-22 10:07:55 -07:00
<FlipNumbers
2022-09-22 22:09:38 -07:00
height={48}
width={32}
2022-07-22 10:07:55 -07:00
play
2022-07-22 14:39:02 -07:00
numbers={formatFixedDecimals(
2022-07-22 10:07:55 -07:00
chartData[chartData.length - 1]['price']
)}
/>
2022-07-10 19:01:16 -07:00
<span
2022-09-07 18:52:47 -07:00
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}
2022-07-10 19:01:16 -07:00
>
2022-10-05 22:11:28 -07:00
<Change change={calculateChartChange()} />
2022-07-10 19:01:16 -07:00
</span>
</div>
2022-07-24 18:47:12 -07:00
<p className="text-sm text-th-fgd-4">
2022-11-05 04:47:54 -07:00
{dayjs(
chartData[chartData.length - 1]['time'] * 1000
).format('DD MMM YY, h:mma')}
2022-07-15 05:50:29 -07:00
</p>
2022-07-10 19:01:16 -07:00
</>
)}
</div>
2022-07-19 20:33:30 -07:00
</div>
2022-10-30 04:19:04 -07:00
<div className="mt-2 h-40 w-auto md:h-72">
2022-09-19 18:09:34 -07:00
<div className="absolute top-[2px] right-0 -mb-2 flex justify-end">
2022-09-07 23:25:32 -07:00
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
values={[1, 7, 30]}
onChange={(v) => setDaysToShow(v)}
/>
2022-07-10 19:01:16 -07:00
</div>
<div className="h-full md:-mx-2 md:mt-4">
2022-09-14 12:38:43 -07:00
<ResponsiveContainer>
2022-07-12 20:58:13 -07:00
<AreaChart
data={chartData}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
// margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
2022-07-12 20:58:13 -07:00
>
<Tooltip
cursor={{
strokeOpacity: 0.09,
}}
content={<></>}
/>
<defs>
<linearGradient
id="gradientArea"
x1="0"
y1="0"
x2="0"
y2="1"
>
2022-07-15 13:26:29 -07:00
<stop
offset="0%"
2022-08-02 17:17:42 -07:00
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
2022-09-14 12:38:43 -07:00
stopOpacity={0.25}
2022-07-15 13:26:29 -07:00
/>
<stop
offset="99%"
2022-08-02 17:17:42 -07:00
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
2022-07-15 13:26:29 -07:00
stopOpacity={0}
/>
</linearGradient>
</defs>
2022-07-12 20:58:13 -07:00
<Area
isAnimationActive={false}
2022-07-12 20:58:13 -07:00
type="monotone"
dataKey="price"
2022-08-02 17:17:42 -07:00
stroke={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
2022-07-18 20:58:21 -07:00
strokeWidth={1.5}
2022-07-12 20:58:13 -07:00
fill="url(#gradientArea)"
2022-09-14 12:38:43 -07:00
label={<CustomizedLabel chartData={chartData} />}
2022-07-12 20:58:13 -07:00
/>
2022-09-14 12:38:43 -07:00
<XAxis dataKey="time" hide padding={{ left: 0, right: 0 }} />
2022-07-12 20:58:13 -07:00
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
padding={{ top: 20, bottom: 20 }}
/>
</AreaChart>
</ResponsiveContainer>
2022-07-10 19:01:16 -07:00
</div>
2022-07-19 20:33:30 -07:00
</div>
2022-07-10 19:01:16 -07:00
</div>
) : (
2022-08-29 22:03:08 -07:00
<div className="mt-4 flex h-full items-center justify-center rounded-lg bg-th-bkg-2 p-4 text-th-fgd-3 md:mt-0">
2022-07-22 10:07:55 -07:00
<div className="">
2022-07-23 21:27:54 -07:00
<LineChartIcon className="mx-auto h-10 w-10 text-th-fgd-4" />
<p className="text-th-fgd-4">Chart not available</p>
2022-07-22 10:07:55 -07:00
</div>
2022-07-10 19:01:16 -07:00
</div>
)}
</ContentBox>
)
}
export default SwapTokenChart