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

760 lines
24 KiB
TypeScript
Raw Normal View History

2023-11-19 17:26:42 -08:00
import React, { MouseEventHandler, useCallback, 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,
2023-04-13 22:22:39 -07:00
Tooltip as RechartsTooltip,
2022-07-12 20:58:13 -07:00
ResponsiveContainer,
2022-09-14 12:38:43 -07:00
Text,
2023-04-13 22:22:39 -07:00
ReferenceDot,
ReferenceDotProps,
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 ContentBox from '../shared/ContentBox'
import { formatCurrencyValue, formatNumericValue } 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'
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-23 18:30:20 -08:00
import useLocalStorageState from 'hooks/useLocalStorageState'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
2022-11-24 18:39:14 -08:00
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
2022-12-11 15:21:40 -08:00
import { useTranslation } from 'next-i18next'
2023-04-13 22:22:39 -07:00
import {
ArrowsRightLeftIcon,
EyeIcon,
EyeSlashIcon,
NoSymbolIcon,
} from '@heroicons/react/20/solid'
2023-01-24 16:54:24 -08:00
import FormatNumericValue from '@components/shared/FormatNumericValue'
2023-02-23 09:28:09 -08:00
import { CategoricalChartFunc } from 'recharts/types/chart/generateCategoricalChart'
2023-04-13 22:22:39 -07:00
import { interpolateNumber } from 'd3-interpolate'
import { IconButton } from '@components/shared/Button'
import Tooltip from '@components/shared/Tooltip'
import { SwapHistoryItem } from 'types'
2023-07-21 11:50:06 -07:00
import useThemeWrapper from 'hooks/useThemeWrapper'
2023-08-13 21:57:03 -07:00
import FavoriteSwapButton from './FavoriteSwapButton'
import { SwapChartDataItem, fetchSwapChartPrices } from 'apis/birdeye/helpers'
2023-11-19 19:50:41 -08:00
import Image from 'next/image'
2022-07-10 19:01:16 -07:00
dayjs.extend(relativeTime)
const set = mangoStore.getState().set
2023-08-03 03:34:57 -07:00
2022-09-14 12:38:43 -07:00
const CustomizedLabel = ({
chartData,
x,
y,
value,
2023-01-05 20:38:13 -08:00
index,
2022-09-14 12:38:43 -07:00
}: {
2023-10-24 03:51:01 -07:00
chartData: SwapChartDataItem[]
2022-09-14 12:38:43 -07:00
x?: number
y?: string | number
value?: number
2023-01-05 20:38:13 -08:00
index?: number
2022-09-14 12:38:43 -07:00
}) => {
const { width } = useViewport()
const [min, max] = useMemo(() => {
if (chartData.length) {
2023-02-23 09:28:09 -08:00
const prices = chartData.map((d) => d.price)
2022-09-14 12:38:43 -07:00
return [Math.min(...prices), Math.max(...prices)]
}
return ['', '']
}, [chartData])
2023-01-05 20:38:13 -08:00
const [minIndex, maxIndex] = useMemo(() => {
const minIndex = chartData.findIndex((d) => d.price === min)
const maxIndex = chartData.findIndex((d) => d.price === max)
return [minIndex, maxIndex]
}, [min, max, chartData])
if (value && (minIndex === index || maxIndex === index)) {
2022-09-14 12:38:43 -07:00
return (
<Text
x={x}
y={y}
dy={value === min ? 16 : -8}
fill="var(--fgd-4)"
2022-09-14 12:38:43 -07:00
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
>
2023-01-24 16:54:24 -08:00
{formatNumericValue(value)}
2022-09-14 12:38:43 -07:00
</Text>
)
} else return <div />
}
interface ExtendedReferenceDotProps extends ReferenceDotProps {
swapHistory: SwapHistoryItem[]
swapMarketName: string
flipPrices: boolean
mouseEnter: (
swap: SwapHistoryItem | undefined,
2023-11-19 17:26:42 -08:00
birdeyePrice: string | number | undefined,
) => void
mouseLeave: MouseEventHandler
}
const SwapHistoryArrows = (props: ExtendedReferenceDotProps) => {
const {
cx,
cy,
x,
y,
swapHistory,
swapMarketName,
flipPrices,
mouseEnter,
mouseLeave,
} = props
const swapDetails = swapHistory.find(
2023-07-21 11:47:53 -07:00
(swap) => dayjs(swap.block_datetime).unix() * 1000 === x,
)
const side =
2023-11-19 17:26:42 -08:00
swapDetails?.swap_in_symbol.toLowerCase() ===
swapMarketName.split('/')[0].toLowerCase()
? !flipPrices
? 'sell'
: 'buy'
: !flipPrices
? 'buy'
: 'sell'
const buy = {
pathCoords: 'M11 0.858312L0.857867 15.0004H21.1421L11 0.858312Z',
fill: 'var(--up)',
yOffset: 0,
}
const sell = {
pathCoords:
'M11 14.1427L21.1421 0.000533306L0.857865 0.000529886L11 14.1427Z',
fill: 'var(--down)',
yOffset: -15,
}
const sideArrowProps =
side === 'buy' ? (!flipPrices ? buy : sell) : !flipPrices ? sell : buy
2023-11-19 17:26:42 -08:00
const birdeyePrice = y ? Number(y) : 0
return cx && cy ? (
<svg
className="cursor-pointer"
width="20"
height="15"
viewBox="0 0 20 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
x={cx - 11}
y={cy + sideArrowProps.yOffset}
2023-11-19 17:26:42 -08:00
onMouseEnter={() => mouseEnter(swapDetails, birdeyePrice)}
onMouseLeave={mouseLeave}
>
<path
d={sideArrowProps.pathCoords}
fill={sideArrowProps.fill}
stroke={'var(--bkg-1)'}
strokeWidth={2}
/>
</svg>
) : (
<div />
)
}
2022-11-18 11:11:06 -08:00
const SwapTokenChart = () => {
2022-12-11 15:21:40 -08:00
const { t } = useTranslation('common')
const {
amountIn,
amountOut,
inputBank,
outputBank,
flipPrices,
swapMode,
swapOrTrigger,
} = mangoStore((s) => s.swap)
2023-10-29 03:57:08 -07:00
const { isDesktop } = useViewport()
2023-10-24 03:51:01 -07:00
const [mouseData, setMouseData] = useState<SwapChartDataItem>()
2022-11-29 21:30:18 -08:00
const [daysToShow, setDaysToShow] = useState('1')
2023-07-21 11:50:06 -07:00
const { theme } = useThemeWrapper()
2022-11-23 18:30:20 -08:00
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
2023-07-21 11:47:53 -07:00
INITIAL_ANIMATION_SETTINGS,
2022-11-23 18:30:20 -08:00
)
2023-04-13 22:22:39 -07:00
const swapHistory = mangoStore((s) => s.mangoAccount.swapHistory.data)
const loadSwapHistory = mangoStore((s) => s.mangoAccount.swapHistory.loading)
2023-04-23 18:29:36 -07:00
const [showSwaps, setShowSwaps] = useState(true)
const [swapTooltipData, setSwapTooltipData] =
useState<SwapHistoryItem | null>(null)
2023-11-19 17:26:42 -08:00
const [swapTooltipBirdeyePrice, setSwapTooltipBirdeyePrice] = useState<
string | number | undefined
>(undefined)
const [inputBankName, outputBankName] = useMemo(() => {
if (!inputBank || !outputBank) return ['', '']
return [inputBank.name, outputBank.name]
}, [inputBank, outputBank])
2023-08-03 03:34:57 -07:00
const swapMarketName = useMemo(() => {
if (!inputBankName || !outputBankName) return ''
const inputSymbol = formatTokenSymbol(inputBankName)
const outputSymbol = formatTokenSymbol(outputBankName)
2023-08-08 06:26:12 -07:00
return flipPrices
2023-08-03 03:34:57 -07:00
? `${outputSymbol}/${inputSymbol}`
: `${inputSymbol}/${outputSymbol}`
}, [flipPrices, inputBankName, outputBankName])
2023-08-03 03:34:57 -07:00
2023-07-20 17:45:03 -07:00
const handleSwapMouseEnter = useCallback(
(
swap: SwapHistoryItem | undefined,
2023-11-19 17:26:42 -08:00
birdeyePrice: string | number | undefined,
2023-07-20 17:45:03 -07:00
) => {
if (swap) {
setSwapTooltipData(swap)
}
2023-11-19 17:26:42 -08:00
if (birdeyePrice) {
setSwapTooltipBirdeyePrice(birdeyePrice)
2023-07-20 17:45:03 -07:00
}
},
2023-11-19 17:26:42 -08:00
[setSwapTooltipData, setSwapTooltipBirdeyePrice],
2023-07-20 17:45:03 -07:00
)
2023-07-20 17:45:03 -07:00
const handleSwapMouseLeave = useCallback(() => {
setSwapTooltipData(null)
2023-07-20 17:45:03 -07:00
}, [setSwapTooltipData])
2023-07-20 17:45:03 -07:00
const renderTooltipContent = useCallback(
(swap: SwapHistoryItem) => {
const {
swap_in_amount,
swap_in_symbol,
swap_out_price_usd,
swap_out_amount,
swap_out_symbol,
} = swap
const swapOutValue = swap_out_price_usd * swap_out_amount
2023-11-19 17:26:42 -08:00
const baseMarketToken = swapMarketName.split('/')[0].toLowerCase()
2023-07-20 17:45:03 -07:00
const swapSide =
2023-11-19 17:26:42 -08:00
swap_in_symbol.toLowerCase() === baseMarketToken
2023-07-20 17:45:03 -07:00
? !flipPrices
? 'sell'
: 'buy'
: !flipPrices
? 'buy'
: 'sell'
const buy = {
price: swap_in_amount / swap_out_amount,
2023-11-19 17:26:42 -08:00
priceSymbol: formatTokenSymbol(swap_in_symbol),
2023-07-20 17:45:03 -07:00
amount: swap_out_amount,
side: 'buy',
2023-11-19 17:26:42 -08:00
symbol: formatTokenSymbol(swap_out_symbol),
2023-07-20 17:45:03 -07:00
value: swapOutValue,
}
2023-07-20 17:45:03 -07:00
const sell = {
price: swap_out_amount / swap_in_amount,
2023-11-19 17:26:42 -08:00
priceSymbol: formatTokenSymbol(swap_out_symbol),
2023-07-20 17:45:03 -07:00
amount: swap_in_amount,
side: 'sell',
2023-11-19 17:26:42 -08:00
symbol: formatTokenSymbol(swap_in_symbol),
2023-07-20 17:45:03 -07:00
value: swapOutValue,
}
2023-07-20 17:45:03 -07:00
const swapProps =
swapSide === 'buy'
? !flipPrices
? buy
: sell
: !flipPrices
? sell
: buy
const { amount, price, priceSymbol, side, symbol, value } = swapProps
2023-11-19 17:26:42 -08:00
let birdeyePercentageDifference = 0
2023-07-20 17:45:03 -07:00
if (
2023-11-19 17:26:42 -08:00
swapTooltipBirdeyePrice &&
typeof swapTooltipBirdeyePrice === 'number'
2023-07-20 17:45:03 -07:00
) {
2023-11-19 17:26:42 -08:00
const difference = ((price - swapTooltipBirdeyePrice) / price) * 100
birdeyePercentageDifference = difference
2023-07-20 17:45:03 -07:00
}
2022-11-18 11:11:06 -08:00
2023-11-19 17:26:42 -08:00
const betterThanBirdeye =
2023-07-20 17:45:03 -07:00
swapSide === 'buy'
? flipPrices
2023-11-19 17:26:42 -08:00
? birdeyePercentageDifference > 0
: birdeyePercentageDifference < 0
2023-07-20 17:45:03 -07:00
: flipPrices
2023-11-19 17:26:42 -08:00
? birdeyePercentageDifference < 0
: birdeyePercentageDifference > 0
2023-07-20 17:45:03 -07:00
return (
<>
<p className="text-center text-th-fgd-2">{`${t(
2023-07-21 11:47:53 -07:00
side,
2023-07-20 17:45:03 -07:00
)} ${amount} ${symbol} at ${formatNumericValue(
2023-07-21 11:47:53 -07:00
price,
2023-07-20 17:45:03 -07:00
)} ${priceSymbol} for ${formatCurrencyValue(value)}`}</p>
2023-11-19 17:26:42 -08:00
{birdeyePercentageDifference ? (
2023-07-20 17:45:03 -07:00
<p
className={`mt-0.5 text-center text-xs ${
2023-11-19 17:26:42 -08:00
betterThanBirdeye ? 'text-th-up' : 'text-th-down'
2023-07-20 17:45:03 -07:00
}`}
>
<span className="font-mono">
2023-11-19 17:26:42 -08:00
{birdeyePercentageDifference.toFixed(2)}%
2023-07-20 17:45:03 -07:00
</span>{' '}
2023-11-19 17:26:42 -08:00
{betterThanBirdeye ? 'better than' : 'worse than'} Birdeye
2023-07-20 17:45:03 -07:00
</p>
) : null}
</>
)
},
2023-11-19 17:26:42 -08:00
[flipPrices, swapMarketName, swapTooltipBirdeyePrice],
2023-07-20 17:45:03 -07:00
)
2022-11-18 11:11:06 -08:00
const {
data: birdeyePriceData,
isLoading: loadingBirdeye,
isFetching: fetchingBirdeye,
} = useQuery(
['swap-chart-price-data', inputBank?.mint, outputBank?.mint, daysToShow],
() =>
fetchSwapChartPrices(
inputBank?.mint.toString(),
outputBank?.mint.toString(),
daysToShow,
),
2022-11-18 20:59:06 -08:00
{
2022-12-15 12:43:20 -08:00
cacheTime: 1000 * 60 * 15,
2022-11-18 20:59:06 -08:00
staleTime: 1000 * 60 * 1,
enabled: !!(inputBank && outputBank),
2023-01-20 08:13:03 -08:00
refetchOnWindowFocus: false,
2023-07-21 11:47:53 -07:00
},
2022-10-07 16:39:06 -07:00
)
2023-01-12 20:26:07 -08:00
2023-11-19 16:31:56 -08:00
const handleFlippedPriceData = useMemo(() => {
if (
!birdeyePriceData ||
!birdeyePriceData.length ||
birdeyePriceData.length < 2
)
return []
if (flipPrices) {
const flippedPrices = []
for (const item of birdeyePriceData) {
const flippedPrice = item.outputTokenPrice / item.inputTokenPrice
flippedPrices.push({ ...item, price: flippedPrice })
}
return flippedPrices
} else {
return birdeyePriceData
}
}, [birdeyePriceData, flipPrices])
2023-04-13 22:22:39 -07:00
const chartSwapTimes = useMemo(() => {
if (
loadSwapHistory ||
!swapHistory ||
!swapHistory.length ||
!inputBankName ||
!outputBankName
)
2023-04-13 22:22:39 -07:00
return []
2023-04-23 18:51:46 -07:00
const chartSymbols = [
inputBankName === 'ETH (Portal)' ? 'ETH' : inputBankName,
outputBankName === 'ETH (Portal)' ? 'ETH' : outputBankName,
2023-04-23 18:51:46 -07:00
]
2023-04-13 22:22:39 -07:00
return swapHistory
.filter(
(swap) =>
chartSymbols.includes(swap.swap_in_symbol) &&
2023-07-21 11:47:53 -07:00
chartSymbols.includes(swap.swap_out_symbol),
2023-04-13 22:22:39 -07:00
)
.map((val) => dayjs(val.block_datetime).unix() * 1000)
}, [swapHistory, loadSwapHistory, inputBankName, outputBankName])
2023-04-13 22:22:39 -07:00
const swapHistoryPoints = useMemo(() => {
2023-11-19 16:31:56 -08:00
if (!handleFlippedPriceData.length || !chartSwapTimes.length) return []
2023-04-13 22:22:39 -07:00
return chartSwapTimes.map((x) => {
2023-10-24 03:51:01 -07:00
const makeSwapChartDataItem = { inputTokenPrice: 1, outputTokenPrice: 1 }
2023-11-19 16:31:56 -08:00
const index = handleFlippedPriceData.findIndex((d) => d.time > x) // find index of data point with x value greater than highlight x
2023-04-13 22:22:39 -07:00
if (index === 0) {
2023-10-24 03:51:01 -07:00
return {
time: x,
2023-11-19 16:31:56 -08:00
price: handleFlippedPriceData[0].price,
2023-10-24 03:51:01 -07:00
...makeSwapChartDataItem,
} // return first data point y value if highlight x is less than first data point x
2023-04-13 22:22:39 -07:00
} else if (index === -1) {
return {
time: x,
2023-11-19 16:31:56 -08:00
price:
handleFlippedPriceData[handleFlippedPriceData.length - 1].price,
2023-10-24 03:51:01 -07:00
...makeSwapChartDataItem,
2023-04-13 22:22:39 -07:00
} // return last data point y value if highlight x is greater than last data point x
} else {
2023-11-19 16:31:56 -08:00
const x0 = handleFlippedPriceData[index - 1].time
const x1 = handleFlippedPriceData[index].time
const y0 = handleFlippedPriceData[index - 1].price
const y1 = handleFlippedPriceData[index].price
2023-04-13 22:22:39 -07:00
const interpolateY = interpolateNumber(y0, y1) // create interpolate function for y values
const y = interpolateY((x - x0) / (x1 - x0)) // estimate y value at highlight x using interpolate function
2023-10-24 03:51:01 -07:00
return { time: x, price: y, ...makeSwapChartDataItem }
2023-04-13 22:22:39 -07:00
}
})
2023-11-19 16:31:56 -08:00
}, [handleFlippedPriceData, chartSwapTimes])
2023-04-13 22:22:39 -07:00
const chartData = useMemo(() => {
2023-11-19 16:31:56 -08:00
if (!handleFlippedPriceData.length) return []
const minTime = handleFlippedPriceData[0].time
const maxTime =
handleFlippedPriceData[handleFlippedPriceData.length - 1].time
let data = handleFlippedPriceData
if (swapHistoryPoints.length && showSwaps) {
const swapPoints = swapHistoryPoints.filter(
2023-07-21 11:47:53 -07:00
(point) => point.time >= minTime && point.time <= maxTime,
2023-04-13 22:22:39 -07:00
)
2023-11-19 16:31:56 -08:00
data = handleFlippedPriceData
.concat(swapPoints)
.sort((a, b) => a.time - b.time)
}
if (amountIn && amountOut && swapOrTrigger === 'swap') {
const latestPrice = flipPrices
? parseFloat(amountIn) / parseFloat(amountOut)
: parseFloat(amountOut) / parseFloat(amountIn)
const item: SwapChartDataItem[] = [
{
price: latestPrice,
2023-11-19 16:31:56 -08:00
time: Date.now(),
inputTokenPrice: 0,
outputTokenPrice: 0,
},
]
return data.concat(item)
} else if (inputBank && outputBank) {
const latestPrice = flipPrices
? outputBank.uiPrice / inputBank.uiPrice
: inputBank.uiPrice / outputBank.uiPrice
const item: SwapChartDataItem[] = [
{
price: latestPrice,
2023-11-19 16:31:56 -08:00
time: Date.now(),
inputTokenPrice: inputBank.uiPrice,
outputTokenPrice: outputBank.uiPrice,
},
]
return data.concat(item)
}
return data
}, [
amountIn,
amountOut,
2023-11-19 16:31:56 -08:00
handleFlippedPriceData,
flipPrices,
inputBank,
outputBank,
showSwaps,
swapHistoryPoints,
swapOrTrigger,
])
2023-04-13 22:22:39 -07:00
2023-07-20 17:45:03 -07:00
const handleMouseMove: CategoricalChartFunc = useCallback(
(coords) => {
if (coords.activePayload) {
setMouseData(coords.activePayload[0].payload)
}
},
2023-07-21 11:47:53 -07:00
[setMouseData],
2023-07-20 17:45:03 -07:00
)
2022-07-10 19:01:16 -07:00
2023-07-20 17:45:03 -07:00
const handleMouseLeave = useCallback(() => {
2023-02-23 09:28:09 -08:00
setMouseData(undefined)
2023-07-20 17:45:03 -07:00
}, [setMouseData])
2022-07-10 19:01:16 -07:00
2023-07-20 17:45:03 -07:00
const calculateChartChange = useCallback(() => {
if (!chartData?.length) return 0
if (mouseData) {
const index = chartData.findIndex((d) => d.time === mouseData.time)
2023-08-03 20:29:04 -07:00
if (index === -1) return 0
return (
((chartData[index]['price'] - chartData[0]['price']) /
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
}
2023-07-20 17:45:03 -07:00
}, [chartData, mouseData])
2023-01-12 20:26:07 -08:00
const loadingSwapPrice = useMemo(() => {
return (
!!(swapMode === 'ExactIn' && Number(amountIn) && !Number(amountOut)) ||
!!(swapMode === 'ExactOut' && Number(amountOut) && !Number(amountIn))
)
}, [amountIn, amountOut, swapMode])
2023-10-29 03:57:08 -07:00
const chartNumberHeight = isDesktop ? 48 : 40
const chartNumberWidth = isDesktop ? 35 : 27
2022-07-10 19:01:16 -07:00
return (
2023-10-29 03:57:08 -07:00
<ContentBox hideBorder hidePadding className="h-full px-4 py-3 md:px-6">
{loadingBirdeye || fetchingBirdeye ? (
<>
<SheenLoader className="w-[148px] rounded-md">
<div className="h-[18px] bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="mt-2 w-[180px] rounded-md">
<div className="h-[40px] bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="mt-2 w-[148px] rounded-md">
<div className="h-[18px] bg-th-bkg-2" />
</SheenLoader>
2023-11-01 19:24:45 -07:00
{!isDesktop ? (
<SheenLoader className="mt-2 w-full rounded-md">
<div className="h-44 bg-th-bkg-2 sm:h-52" />
</SheenLoader>
) : null}
</>
2023-11-19 17:26:42 -08:00
) : chartData?.length ? (
<div className="relative h-full">
{swapTooltipData ? (
<div className="absolute bottom-2 left-1/2 z-10 w-full -translate-x-1/2 rounded-md border border-th-bkg-3 bg-th-bkg-1 px-4 py-2">
{renderTooltipContent(swapTooltipData)}
</div>
) : null}
2022-07-10 19:01:16 -07:00
<div className="flex items-start justify-between">
<div>
{inputBankName && outputBankName ? (
2022-08-18 06:59:36 -07:00
<div className="mb-0.5 flex items-center">
2023-11-19 19:50:41 -08:00
<Tooltip content={t('swap:tooltip-price-chart-source')}>
<Image
className="mr-1.5 cursor-help"
src="/images/birdeye.png"
alt="birdeye"
height={20}
width={20}
/>
</Tooltip>
2023-01-12 20:26:07 -08:00
<p className="text-base text-th-fgd-3">{swapMarketName}</p>
2023-04-13 22:22:39 -07:00
<IconButton
className="px-2 text-th-fgd-3"
2023-08-03 03:34:57 -07:00
onClick={() =>
set((state) => {
state.swap.flipPrices = !flipPrices
})
2023-08-03 03:34:57 -07:00
}
2023-04-13 22:22:39 -07:00
hideBg
>
2023-04-13 22:22:39 -07:00
<ArrowsRightLeftIcon className="h-5 w-5" />
</IconButton>
2022-08-18 06:59:36 -07:00
</div>
2022-07-10 19:01:16 -07:00
) : null}
{mouseData ? (
<>
2023-10-29 03:57:08 -07:00
<div className="mb-1 flex flex-col font-display text-4xl text-th-fgd-1 md:flex-row md:items-end md:text-5xl">
2022-11-23 18:30:20 -08:00
{animationSettings['number-scroll'] ? (
<FlipNumbers
2023-10-29 03:57:08 -07:00
height={chartNumberHeight}
width={chartNumberWidth}
2022-11-23 18:30:20 -08:00
play
2023-02-23 09:28:09 -08:00
numbers={formatNumericValue(mouseData.price)}
2022-11-23 18:30:20 -08:00
/>
) : (
2023-10-24 21:03:35 -07:00
<span className="tabular-nums">
<FormatNumericValue value={mouseData.price} />
</span>
2022-11-23 18:30:20 -08:00
)}
2022-07-10 19:01:16 -07:00
</div>
2023-10-29 03:57:08 -07:00
<div className="flex space-x-3">
<Change change={calculateChartChange()} suffix="%" />
<p className="text-sm text-th-fgd-4">
{dayjs(mouseData.time).format('DD MMM YY, h:mma')}
</p>
</div>
2022-07-10 19:01:16 -07:00
</>
) : (
<>
2023-10-29 03:57:08 -07:00
<div className="mb-1 flex flex-col font-display text-4xl text-th-fgd-1 md:flex-row md:items-end md:text-5xl">
{loadingSwapPrice ? (
<SheenLoader className="mt-2 w-[180px] rounded-md">
2023-10-29 22:11:41 -07:00
<div className="h-9 bg-th-bkg-2 md:h-10" />
</SheenLoader>
) : animationSettings['number-scroll'] ? (
2022-11-23 18:30:20 -08:00
<FlipNumbers
2023-10-29 03:57:08 -07:00
height={chartNumberHeight}
width={chartNumberWidth}
2022-11-23 18:30:20 -08:00
play
2023-01-24 16:54:24 -08:00
numbers={formatNumericValue(
2023-07-21 11:47:53 -07:00
chartData[chartData.length - 1].price,
2022-11-23 18:30:20 -08:00
)}
/>
) : (
2023-10-24 21:03:35 -07:00
<span className="tabular-nums">
<FormatNumericValue
value={chartData[chartData.length - 1].price}
/>
</span>
2022-11-23 18:30:20 -08:00
)}
2022-07-10 19:01:16 -07:00
</div>
2023-10-29 03:57:08 -07:00
<div className="flex space-x-3">
<Change change={calculateChartChange()} suffix="%" />
<p className="text-sm text-th-fgd-4">
{dayjs(chartData[chartData.length - 1].time).format(
'DD MMM YY, h:mma',
)}
</p>
</div>
2022-07-10 19:01:16 -07:00
</>
)}
</div>
2022-07-19 20:33:30 -07:00
</div>
2023-10-29 03:57:08 -07:00
<div className="mt-2 h-44 w-auto sm:h-52 md:h-96">
<div className="absolute right-0 top-[2px] -mb-2 flex items-center justify-end space-x-4">
2023-08-13 21:57:03 -07:00
<FavoriteSwapButton
inputToken={inputBank!.name}
outputToken={outputBank!.name}
/>
2023-04-13 22:22:39 -07:00
<Tooltip
content={
showSwaps
? t('swap:hide-swap-history')
: t('swap:show-swap-history')
}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => setShowSwaps(!showSwaps)}
>
{showSwaps ? (
<EyeIcon className="h-5 w-5" />
) : (
<EyeSlashIcon className="h-5 w-5" />
)}
</IconButton>
</Tooltip>
2022-09-07 23:25:32 -07:00
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
2022-11-29 21:30:18 -08:00
values={['1', '7', '30']}
2022-09-07 23:25:32 -07:00
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}
>
2023-04-13 22:22:39 -07:00
<RechartsTooltip
2022-07-12 20:58:13 -07:00
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
2022-11-30 19:32:32 -08:00
? COLORS.UP[theme]
: COLORS.DOWN[theme]
2022-08-02 17:17:42 -07:00
}
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
2022-11-30 19:32:32 -08:00
? COLORS.UP[theme]
: COLORS.DOWN[theme]
2022-08-02 17:17:42 -07:00
}
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
2022-11-30 19:32:32 -08:00
? COLORS.UP[theme]
: COLORS.DOWN[theme]
2022-08-02 17:17:42 -07:00
}
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 }}
/>
{showSwaps && swapHistoryPoints.length
? swapHistoryPoints.map((point, index) => (
2023-04-13 22:22:39 -07:00
<ReferenceDot
key={index}
x={point.time}
y={point.price}
isFront={true}
shape={
<SwapHistoryArrows
swapHistory={swapHistory}
swapMarketName={swapMarketName}
flipPrices={flipPrices}
mouseEnter={handleSwapMouseEnter}
mouseLeave={handleSwapMouseLeave}
/>
}
2023-04-13 22:22:39 -07:00
/>
))
: null}
2022-07-12 20:58:13 -07:00
</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-12-11 15:21:40 -08:00
<div className="mt-4 flex h-full items-center justify-center p-4 text-th-fgd-3 md:mt-0">
2022-07-22 10:07:55 -07:00
<div className="">
2022-12-11 15:21:40 -08:00
<NoSymbolIcon className="mx-auto mb-1 h-6 w-6 text-th-fgd-4" />
<p className="text-th-fgd-4">{t('chart-unavailable')}</p>
2022-07-22 10:07:55 -07:00
</div>
2022-07-10 19:01:16 -07:00
</div>
)}
</ContentBox>
)
}
export default React.memo(SwapTokenChart)