add simple area chart

This commit is contained in:
saml33 2022-08-03 10:17:42 +10:00
parent ba39db5b9f
commit 0f46288df6
6 changed files with 172 additions and 22 deletions

View File

@ -3,12 +3,14 @@ import { Transition } from '@headlessui/react'
import { ChevronDownIcon, DotsHorizontalIcon } from '@heroicons/react/solid'
import { useWallet } from '@solana/wallet-adapter-react'
import { useTranslation } from 'next-i18next'
import { useTheme } from 'next-themes'
import Image from 'next/image'
import { useRouter } from 'next/router'
import { Fragment, useEffect, useMemo, useState } from 'react'
import { useViewport } from '../hooks/useViewport'
import mangoStore from '../store/state'
import { COLORS } from '../styles/colors'
import { formatDecimal, numberFormat } from '../utils/numbers'
import { breakpoints } from '../utils/theme'
import Switch from './forms/Switch'
@ -19,6 +21,7 @@ import { IconButton, LinkButton } from './shared/Button'
import ContentBox from './shared/ContentBox'
import { UpTriangle } from './shared/DirectionTriangles'
import IconDropMenu from './shared/IconDropMenu'
import SimpleAreaChart from './shared/SimpleAreaChart'
import { FadeInList } from './shared/Transitions'
const TokenList = () => {
@ -27,10 +30,20 @@ const TokenList = () => {
const [showTokenDetails, setShowTokenDetails] = useState('')
const [showZeroBalances, setShowZeroBalances] = useState(true)
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
const loadingCoingeckoPrices = mangoStore((s) => s.coingeckoPrices.loading)
const actions = mangoStore((s) => s.actions)
const group = mangoStore((s) => s.group)
const { theme } = useTheme()
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
useEffect(() => {
if (coingeckoPrices.length === 0) {
actions.fetchCoingeckoPrices()
}
}, [coingeckoPrices])
const banks = useMemo(() => {
if (group?.banksMap) {
const rawBanks = Array.from(group?.banksMap, ([key, value]) => ({
@ -85,21 +98,27 @@ const TokenList = () => {
<table className="min-w-full">
<thead>
<tr>
<th className="w-[12.5%] text-left">{t('token')}</th>
<th className="w-[12.5%] text-right">{t('price')}</th>
{/* <th className="w-[12.5%] text-right">{t('rolling-change')}</th>
<th className="w-[12.5%] text-right">{t('daily-volume')}</th> */}
<th className="w-[12.5%] text-right">{t('rates')}</th>
<th className="w-[12.5%] text-right">{t('liquidity')}</th>
<th className="w-[12.5%] text-right">{t('available-balance')}</th>
<th className="w-[16.67%] text-left">{t('token')}</th>
<th className="w-[16.67%] text-right">{t('price')}</th>
<th className="className='hidden lg:block' w-[16.67%] text-right"></th>
<th className="w-[16.67%] text-right">{t('rates')}</th>
<th className="w-[16.67%] text-right">{t('liquidity')}</th>
<th className="w-[16.67%] text-right">
{t('available-balance')}
</th>
</tr>
</thead>
<tbody>
{banks.map((bank, index) => {
const oraclePrice = bank.value.price
const coingeckoData = coingeckoPrices.find(
(asset) => asset.symbol === bank.key
)
const chartData = coingeckoData ? coingeckoData.prices : undefined
return (
<FadeInList as="tr" index={index} key={bank.key}>
<td className="w-[12.5%]">
<td className="w-[16.67%]">
<div className="flex items-center">
<div className="mr-2.5 flex flex-shrink-0 items-center">
<Image
@ -112,7 +131,7 @@ const TokenList = () => {
<p>{bank.value.name}</p>
</div>
</td>
<td className="w-[12.5%]">
<td className="w-[16.67%]">
<div className="flex flex-col text-right">
<p>${formatDecimal(oraclePrice.toNumber(), 2)}</p>
{/* <div className="flex items-center justify-end">
@ -121,17 +140,37 @@ const TokenList = () => {
</div> */}
</div>
</td>
{/* <td className="w-[12.5%]">
<td className="hidden lg:block">
<div>
{!loadingCoingeckoPrices ? (
chartData !== undefined ? (
<SimpleAreaChart
// update color when we have 24h change
color={COLORS.GREEN[theme]}
data={chartData}
height={40}
name={bank.key}
width={104}
/>
) : bank.key === 'USDC' ? null : (
t('unavailable')
)
) : (
<div className="h-10 w-[104px] animate-pulse rounded bg-th-bkg-3" />
)}
</div>
</td>
{/* <td className="w-[16.67%]">
<div className="flex flex-col text-right">
<p className="text-th-green">0%</p>
</div>
</td> */}
{/* <td className="w-[12.5%]">
{/* <td className="w-[16.67%]">
<div className="flex flex-col text-right">
<p>1000</p>
</div>
</td> */}
<td className="w-[12.5%]">
<td className="w-[16.67%]">
<div className="flex justify-end space-x-2 text-right">
<p className="text-th-green">
{formatDecimal(
@ -150,7 +189,7 @@ const TokenList = () => {
</p>
</div>
</td>
<td className="w-[12.5%]">
<td className="w-[16.67%]">
<div className="flex flex-col text-right">
<p>
{formatDecimal(
@ -160,7 +199,7 @@ const TokenList = () => {
</p>
</div>
</td>
<td className="w-[12.5%] pt-4 text-right">
<td className="w-[16.67%] pt-4 text-right">
<p className="px-2">
{mangoAccount
? formatDecimal(mangoAccount.getUi(bank.value))
@ -176,7 +215,7 @@ const TokenList = () => {
: '$0'}
</p>
</td>
<td className="w-[12.5%]">
<td className="w-[16.67%]">
<div className="flex justify-end space-x-2">
<ActionsMenu
bank={bank.value}

View File

@ -0,0 +1,37 @@
import { Area, AreaChart, XAxis, YAxis } from 'recharts'
const SimpleAreaChart = ({
color,
data,
height,
name,
width,
}: {
color: string
data: any[]
height: number
name: string
width: number
}) => {
return (
<AreaChart width={width} height={height} data={data}>
<defs>
<linearGradient id={`gradientArea-${name}`} x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={color} stopOpacity={0.6} />
<stop offset="100%" stopColor={color} stopOpacity={0} />
</linearGradient>
</defs>
<Area
isAnimationActive={false}
type="monotone"
dataKey="1"
stroke={color}
fill={`url(#gradientArea-${name})`}
/>
<XAxis dataKey="0" hide />
<YAxis domain={['dataMin', 'dataMax']} dataKey="1" hide />
</AreaChart>
)
}
export default SimpleAreaChart

View File

@ -13,10 +13,11 @@ import FlipNumbers from 'react-flip-numbers'
import LineChartIcon from '../icons/LineChartIcon'
import ContentBox from '../shared/ContentBox'
import { GREEN, RED } from '../../styles/colors'
import { DownTriangle, UpTriangle } from '../shared/DirectionTriangles'
import { formatFixedDecimals } from '../../utils/numbers'
import SheenLoader from '../shared/SheenLoader'
import { COLORS } from '../../styles/colors'
import { useTheme } from 'next-themes'
dayjs.extend(relativeTime)
@ -85,6 +86,7 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
const [outputTokenInfo, setOutputTokenInfo] = useState<any>(null)
const [mouseData, setMouseData] = useState<any>(null)
const [daysToShow, setDaysToShow] = useState(1)
const { theme } = useTheme()
const handleMouseMove = (coords: any) => {
if (coords.activePayload) {
@ -295,12 +297,20 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
>
<stop
offset="0%"
stopColor={calculateChartChange() >= 0 ? GREEN : RED}
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
stopOpacity={0.15}
/>
<stop
offset="99%"
stopColor={calculateChartChange() >= 0 ? GREEN : RED}
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
stopOpacity={0}
/>
</linearGradient>
@ -309,7 +319,11 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
isAnimationActive={false}
type="monotone"
dataKey="price"
stroke={calculateChartChange() >= 0 ? GREEN : RED}
stroke={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
strokeWidth={1.5}
fill="url(#gradientArea)"
/>

View File

@ -14,6 +14,7 @@ import EmptyWallet from '../utils/wallet'
import { Order } from '@project-serum/serum/lib/market'
import { Notification, notify } from '../utils/notifications'
import {
COINGECKO_IDS,
fetchNftsFromHolaplexIndexer,
getTokenAccountsByOwnerWithWrappedSol,
TokenAccount,
@ -51,6 +52,10 @@ interface NFT {
}
export type MangoStore = {
coingeckoPrices: {
data: any[]
loading: boolean
}
connected: boolean
connection: Connection
group: Group | undefined
@ -82,6 +87,7 @@ export type MangoStore = {
}
}
actions: {
fetchCoingeckoPrices: () => Promise<void>
fetchGroup: () => Promise<void>
fetchMangoAccount: (wallet: Wallet) => Promise<void>
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
@ -98,6 +104,10 @@ export type MangoStore = {
const mangoStore = create<MangoStore>(
subscribeWithSelector((set, get) => {
return {
coingeckoPrices: {
data: [],
loading: false,
},
connected: false,
connection,
group: undefined,
@ -129,6 +139,36 @@ const mangoStore = create<MangoStore>(
},
},
actions: {
fetchCoingeckoPrices: async () => {
const set = get().set
set((state) => {
state.coingeckoPrices.loading = true
})
try {
const promises: any = []
for (const asset of COINGECKO_IDS) {
promises.push(
fetch(
`https://api.coingecko.com/api/v3/coins/${asset.id}/market_chart?vs_currency=usd&days=1`
).then((res) => res.json())
)
}
const data = await Promise.all(promises)
for (let i = 0; i < data.length; i++) {
data[i].symbol = COINGECKO_IDS[i].symbol
}
set((state) => {
state.coingeckoPrices.data = data
state.coingeckoPrices.loading = false
})
} catch (e) {
console.log('ERORR: Unable to load Coingecko prices')
set((state) => {
state.coingeckoPrices.loading = false
})
}
},
fetchGroup: async () => {
try {
const set = get().set

View File

@ -1,3 +1,5 @@
export const PRIMARY = '#F2C94C'
export const GREEN = '#AFD803'
export const RED = '#F84638'
export const COLORS: any = {
GREEN: { Mango: '#AFD803', Dark: '#5EBF4d', Light: '#5EBF4d' },
PRIMARY: { Mango: '#F2C94C', Dark: '#F2C94C', Light: '#FF9C24' },
RED: { Mango: '#F84638', Dark: '#CC2929', Light: '#CC2929' },
}

View File

@ -97,3 +97,21 @@ export const fetchNftsFromHolaplexIndexer = async (owner: PublicKey) => {
const body = await result.json()
return body.data
}
export const COINGECKO_IDS = [
{ id: 'bitcoin', symbol: 'BTC' },
// { id: 'ethereum', symbol: 'ETH' },
{ id: 'solana', symbol: 'SOL' },
// { id: 'mango-markets', symbol: 'MNGO' },
// { id: 'binancecoin', symbol: 'BNB' },
// { id: 'serum', symbol: 'SRM' },
// { id: 'raydium', symbol: 'RAY' },
// { id: 'ftx-token', symbol: 'FTT' },
// { id: 'avalanche-2', symbol: 'AVAX' },
// { id: 'terra-luna', symbol: 'LUNA' },
// { id: 'cope', symbol: 'COPE' },
// { id: 'cardano', symbol: 'ADA' },
// { id: 'msol', symbol: 'MSOL' },
// { id: 'tether', symbol: 'USDT' },
// { id: 'stepn', symbol: 'GMT' },
]