2022-11-18 11:11:06 -08:00
|
|
|
import { useEffect, 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-11-18 08:29:37 -08:00
|
|
|
import { fetchChartData } from 'apis/coingecko'
|
2022-10-10 10:28:53 -07:00
|
|
|
import mangoStore from '@store/mangoStore'
|
2022-11-18 11:11:06 -08:00
|
|
|
import useJupiterSwapData from './useJupiterSwapData'
|
2022-07-10 19:01:16 -07:00
|
|
|
|
|
|
|
dayjs.extend(relativeTime)
|
|
|
|
|
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-11-18 11:11:06 -08:00
|
|
|
const SwapTokenChart = () => {
|
|
|
|
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
|
|
|
const { inputCoingeckoId, outputCoingeckoId } = useJupiterSwapData()
|
|
|
|
const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId)
|
|
|
|
const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId)
|
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-18 11:11:06 -08:00
|
|
|
|
2022-10-07 16:39:06 -07:00
|
|
|
const chartDataQuery = useQuery(
|
2022-11-18 08:29:37 -08:00
|
|
|
['chart-data', baseTokenId, quoteTokenId, daysToShow],
|
|
|
|
() => fetchChartData(baseTokenId, quoteTokenId, daysToShow),
|
2022-11-18 11:11:06 -08:00
|
|
|
{ staleTime: 0, enabled: !!baseTokenId && !!quoteTokenId }
|
2022-10-07 16:39:06 -07:00
|
|
|
)
|
|
|
|
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-11-18 08:29:37 -08:00
|
|
|
useEffect(() => {
|
2022-11-18 11:11:06 -08:00
|
|
|
if (!inputCoingeckoId || !outputCoingeckoId) return
|
2022-11-18 08:29:37 -08:00
|
|
|
|
2022-11-18 11:11:06 -08:00
|
|
|
if (['usd-coin', 'tether'].includes(outputCoingeckoId)) {
|
|
|
|
setBaseTokenId(inputCoingeckoId)
|
|
|
|
setQuoteTokenId(outputCoingeckoId)
|
2022-11-18 08:29:37 -08:00
|
|
|
} else {
|
2022-11-18 11:11:06 -08:00
|
|
|
setBaseTokenId(outputCoingeckoId)
|
|
|
|
setQuoteTokenId(inputCoingeckoId)
|
2022-11-18 08:29:37 -08:00
|
|
|
}
|
2022-11-18 11:11:06 -08:00
|
|
|
}, [inputCoingeckoId, outputCoingeckoId])
|
2022-11-18 08:29:37 -08:00
|
|
|
|
|
|
|
// const handleFlipChart = useCallback(() => {
|
|
|
|
// if (!baseTokenId || !quoteTokenId) return
|
|
|
|
// setBaseTokenId(quoteTokenId)
|
|
|
|
// setQuoteTokenId(baseTokenId)
|
|
|
|
// }, [baseTokenId, quoteTokenId])
|
|
|
|
|
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-11-18 11:11:06 -08:00
|
|
|
{chartDataQuery?.isLoading || chartDataQuery.isFetching ? (
|
2022-08-17 13:05:38 -07:00
|
|
|
<>
|
|
|
|
<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" />
|
2022-08-17 13:05:38 -07:00
|
|
|
</SheenLoader>
|
|
|
|
</>
|
2022-11-18 11:11:06 -08:00
|
|
|
) : chartData?.length && baseTokenId && quoteTokenId ? (
|
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-18 11:11:06 -08:00
|
|
|
{['usd-coin', 'tether'].includes(inputCoingeckoId || '')
|
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>
|
2022-11-18 08:29:37 -08:00
|
|
|
{/* <div
|
|
|
|
className="px-2 hover:cursor-pointer hover:text-th-primary"
|
|
|
|
onClick={handleFlipChart}
|
|
|
|
>
|
|
|
|
<SwitchHorizontalIcon className="h-4 w-4" />
|
|
|
|
</div> */}
|
2022-08-18 06:59:36 -07:00
|
|
|
</div>
|
2022-07-10 19:01:16 -07:00
|
|
|
) : null}
|
|
|
|
{mouseData ? (
|
|
|
|
<>
|
2022-11-18 08:29:37 -08:00
|
|
|
<div className="mb-1 flex flex-col text-4xl 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-18 08:29:37 -08:00
|
|
|
{dayjs(mouseData['time']).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-18 08:29:37 -08:00
|
|
|
{dayjs(chartData[chartData.length - 1]['time']).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>
|
2022-09-14 14:57:12 -07:00
|
|
|
<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}
|
2022-09-14 14:57:12 -07:00
|
|
|
// margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
|
2022-07-12 20:58:13 -07:00
|
|
|
>
|
|
|
|
<Tooltip
|
|
|
|
cursor={{
|
|
|
|
strokeOpacity: 0.09,
|
|
|
|
}}
|
|
|
|
content={<></>}
|
|
|
|
/>
|
2022-07-14 12:33:11 -07:00
|
|
|
<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}
|
|
|
|
/>
|
2022-07-14 12:33:11 -07:00
|
|
|
</linearGradient>
|
|
|
|
</defs>
|
2022-07-12 20:58:13 -07:00
|
|
|
<Area
|
2022-08-01 08:53:09 -07:00
|
|
|
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
|