diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 4416476..35090ad 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -93,7 +93,7 @@ export default function CumulativeInflow() { if (!loading && !errorDailyInflow) { formatData(); } - }, [loading, errorDailyInflow]); + }, [dataDailyInflow, dataCumulativeInflow, loading, errorDailyInflow]); return ( diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 0e65a97..da26abb 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -68,7 +68,7 @@ export default function CumulativeUsers() { if (!loading || !error) { formatData(); } - }, [loading, error]); + }, [dataCumulativeNewUsers, loading, error]); return ( diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index 75a24fb..467ed07 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -1,9 +1,10 @@ -import { useState, useEffect, useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; import Select from 'react-dropdown-select'; import { Box } from '@chakra-ui/react'; import moment from 'moment'; import { DateRange } from 'react-date-range'; -import strftime, { timezone } from 'strftime'; +import strftime from 'strftime'; import { DataContext } from '../../../contexts/data'; import 'react-date-range/dist/styles.css'; import 'react-date-range/dist/theme/default.css'; @@ -13,34 +14,63 @@ const DATA_START_DATE = new Date('2023-06-14T20:00:00.000'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { - const { setDates } = useContext(DataContext); - + const { setDates, dates } = useContext(DataContext); + const searchParams = useSearchParams(); + const router = useRouter(); const [selectedDateRangeOption, setSelectedDateRangeOption] = useState(null); - const [rangeState, setRangeState] = useState< - { startDate: Date | undefined; endDate: Date | undefined; key: string }[] - >([ - { - startDate: DATA_START_DATE, - endDate: DATE_NOW, - key: 'selection', - }, - ]); - console.log({ - startDate: DATA_START_DATE, - endDate: new Date(), + const isValidDate = (dateString: string) => { + return moment(dateString, 'MM-DD-YY', true).isValid(); + }; + + const getInitialDate = (key: string, fallback: Date) => { + const param = searchParams.get(key); + if (!param || !isValidDate(param)) return fallback; + const date = moment(param, 'MM-DD-YY').toDate(); + if (key === 'from' && date < DATA_START_DATE) return DATA_START_DATE; + if (key === 'to' && date > DATE_NOW) return DATE_NOW; + return date; + }; + + const initialFrom = getInitialDate('from', DATA_START_DATE); + const initialTo = getInitialDate('to', DATE_NOW); + const correctedFrom = initialFrom > initialTo ? DATA_START_DATE : initialFrom; + const correctedTo = initialFrom > initialTo ? DATE_NOW : initialTo; + + const [rangeState, setRangeState] = useState({ + startDate: correctedFrom, + endDate: correctedTo, key: 'selection', }); - const [dataRange, setDataRange] = useState({ fromValue: DATA_START_DATE, toValue: DATE_NOW }); + const updateQueryParams = (from: Date, to: Date) => { + const params = new URLSearchParams(searchParams.toString()); + if (from) params.set('from', strftime('%m-%d-%y', from)); + else params.delete('from'); + if (to) params.set('to', strftime('%m-%d-%y', to)); + else params.delete('to'); + router.push(`?${params.toString()}`); + window.history.replaceState(null, '', `?${params.toString()}`); + }; + + useEffect(() => { + updateQueryParams(rangeState.startDate, rangeState.endDate); + setDates({ + from: strftime('%Y-%m-%d', rangeState.startDate), + to: strftime('%Y-%m-%d', rangeState.endDate), + }); + }, []); const onChange = (selectedDates: any) => { const [start, end] = selectedDates; const from = start ? strftime('%Y-%m-%d', new Date(start)) : undefined; const to = end ? strftime('%Y-%m-%d', end) : undefined; - if (from === to) return; - setDataRange({ fromValue: start, toValue: end }); + if (from === to || !from || !to) return; + setRangeState({ startDate: new Date(from), endDate: new Date(to), key: 'selection' }); setDates({ from, to }); + if (from && to) { + updateQueryParams(start, end); + } }; const dateRangeOptions = [ @@ -55,16 +85,6 @@ export const DateRangeSelect = () => { }, ]; - useEffect(() => { - setRangeState([ - { - startDate: dataRange.fromValue, - endDate: dataRange.toValue, - key: 'selection', - }, - ]); - }, [dataRange.fromValue, dataRange.toValue]); - const onSelectItem = (option: { id: number; label: string; isDefault?: boolean }) => { if (option.id == ALL_TIME_ID) { onChange([null, null]); @@ -95,7 +115,6 @@ export const DateRangeSelect = () => { }, []); const onDateRangeChange = (item: any) => { - setRangeState([item.selection]); if (item.selection.startDate == item.selection.endDate) { return; } @@ -105,13 +124,13 @@ export const DateRangeSelect = () => { const customContentRenderer = () => { return (
- {dataRange.fromValue && - dataRange.toValue && - `${strftime('%m-%d-%y', dataRange.fromValue)} to ${strftime( + {rangeState.startDate && + rangeState.endDate && + `${strftime('%m-%d-%y', rangeState.startDate)} to ${strftime( '%m-%d-%y', - dataRange.toValue + rangeState.endDate )}`} - {(!dataRange.fromValue || !dataRange.toValue) && 'All time'} + {(!rangeState.startDate || !rangeState.endDate) && 'All time'}
); }; @@ -124,7 +143,7 @@ export const DateRangeSelect = () => { editableDateInputs={true} onChange={onDateRangeChange} moveRangeOnFirstSelection={false} - ranges={rangeState} + ranges={[rangeState]} showDateDisplay={false} fixedHeight={false} minDate={DATA_START_DATE} diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 88e1d0c..4f1c49d 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -97,7 +97,7 @@ export default function FundingRate() { const uniqueCoins = extractUniqueCoins(dataFundingRate); setCoinKeys(uniqueCoins); } - }, [loading, dataFundingRate]); + }, [dataFundingRate, loading, dataFundingRate]); const formatData = () => { if (dataFundingRate) { @@ -110,7 +110,7 @@ export default function FundingRate() { if (!loading && !error) { formatData(); } - }, [loading, coinsSelected]); + }, [dataFundingRate, loading, coinsSelected]); const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index e0b338c..822e718 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -232,7 +232,7 @@ export default function Hlp() { if (!loading && !error) { formatData(); } - }, [loading, error]); + }, [assetCtxs, dataHlpPositions, dataHlpPnL, loading, error]); return ( diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index 2933bc9..0780fdd 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -229,7 +229,14 @@ export default function LiquidatorChart(props: any) { if (!loading && !error) { formatData(coinsSelected); } - }, [loading, error]); + }, [ + dataCumulativeLiquidated, + dataDailyLiquidatedTotal, + dataDailyLiquidatedByMargin, + dataDailyLiquidatedByCoins, + loading, + error, + ]); const dataModeToData = (dataMode: string) => { switch (dataMode) { diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 60a7abd..319f894 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -232,7 +232,7 @@ export default function Liquidity() { if (!loading && !error) { formatData(); } - }, [loading, coinKeys]); + }, [dataLiqudity, loading, coinKeys]); let chartData; let chartDataCoinKeys; diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 9c12323..3baaccd 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -99,7 +99,7 @@ export default function OpenInterestChart() { if (!loading && !error) { formatData(); } - }, [loading]); + }, [dataOpenInterest, loading]); const coinSelectors = createCoinSelectors( coins, diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 8f5f8ed..595c27f 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -240,7 +240,7 @@ export default function RetailVolumeChart() { if (!loading || error) { formatData(coinsSelected); } - }, [loading, error]); + }, [dataCumulativeUsdVolume, loading, error]); const coinSelectors = createCoinSelectors( coinKeys, diff --git a/components/home/charts/top-stats.tsx b/components/home/charts/top-stats.tsx index 280e52e..3b7d31d 100644 --- a/components/home/charts/top-stats.tsx +++ b/components/home/charts/top-stats.tsx @@ -32,27 +32,24 @@ const TopStats = () => { REQUESTS[0], 0, 'total_users', - true ); - const [dataTotalVolume, loadingVol, errorVol] = useRequest(REQUESTS[1], 0, 'chart_data', true); + const [dataTotalVolume, loadingVol, errorVol] = useRequest(REQUESTS[1], 0, 'chart_data'); const [totalVolume, setTotalVolume] = useState(0); const [dataTotalDeposits, loadingTotalDeposits, errorTotalDeposits] = useRequest( REQUESTS[2], 0, - 'total_deposits', - true + 'total_deposits' ); const [dataTotalWithdrawals, loadingTotalWithdrawals, errorTotalWithdrawals] = useRequest( REQUESTS[3], 0, 'total_withdrawals', - true ); const [ dataTotalNotionalLiquidated, loadingTotalNotionalLiquidated, errorTotalNotionalLiquidated, - ] = useRequest(REQUESTS[4], 0, 'total_notional_liquidated', true); + ] = useRequest(REQUESTS[4], 0, 'total_notional_liquidated'); interface TotalVolume { time: string; @@ -73,7 +70,7 @@ const TopStats = () => { if (!loadingVol && !errorVol && dataTotalVolume) { computeTotalVolume(dataTotalVolume); } - }, [loadingVol, errorVol]); + }, [dataTotalVolume, loadingVol, errorVol]); return ( diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index b32bffa..4a1d922 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -176,7 +176,7 @@ export default function UniqueUsers() { if (!loading && !error) { formatData(coinsSelected); } - }, [loading]); + }, [dataCumulativeNewUsers, dataDailyUniqueUsers, dataDailyUniqueUsersByCoin, loading]); const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index e699631..0871c68 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -233,7 +233,7 @@ export default function VolumeChart() { if (!loading && !error) { formatData(coinsSelected); } - }, [loading, dataMode]); + }, [dataDailyTradesByCoin, loading, dataMode]); const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 2427602..1cc50f2 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -113,7 +113,7 @@ export default function TotalVolumeChart() { if (!loading && !error) { formatData(coinsSelected); } - }, [loading, error]); + }, [dataTotalVolume, loading, error]); const coinSelectors = createCoinSelectors( coins, diff --git a/hooks/useRequest.ts b/hooks/useRequest.ts index 5ec86a6..f5a5002 100644 --- a/hooks/useRequest.ts +++ b/hooks/useRequest.ts @@ -1,69 +1,58 @@ import { useState, useEffect, useContext, useCallback } from 'react'; import { DataContext } from '../contexts/data'; -let runningRequests: number = 0; -const MAX_CONCURRENT_REQUESTS = 4; - const fetcher = (url: string) => fetch(url).then((res) => res.json()); -export function useRequest(url: string, defaultValue: any, key?: string, dontRefetch?: boolean) { - const [loading, setLoading] = useState(true); - const [data, setData] = useState(defaultValue); +export function useRequest( + url: string, + defaultValue: T, + key?: string, +): any { + const [loading, setLoading] = useState(true); + const [responseData, setResponseData] = useState(null); + const [data, setData] = useState(defaultValue); const dataContext = useContext(DataContext); const init = useCallback(async () => { - const request = async () => { - if (runningRequests > MAX_CONCURRENT_REQUESTS) { - setTimeout(() => { - request(); - }, 500); - return; - } - - runningRequests++; - setLoading(true); - try { - const data = await fetcher(`${process.env.NEXT_PUBLIC_DAT_URL}/${url}`); - runningRequests--; - - const dataFromKey = key ? data[key] : data?.table_data || data?.chart_data || data; - setData( - dataFromKey.filter - ? dataFromKey.filter((line: any) => { - if (!line.time) { - return true; - } - if ( - dataContext.dates.from && - new Date(line.time) < new Date(dataContext.dates.from) - ) { - return false; - } - if (dataContext.dates.to && new Date(line.time) > new Date(dataContext.dates.to)) { - return false; - } - return true; - }) - : dataFromKey - ); - setLoading(false); - } catch (e) { - console.log(e); - runningRequests--; - } - }; - - request(); - }, [dataContext.dates.from, dataContext.dates.to, key, url]); + setLoading(true); + try { + const data = await fetcher(`${process.env.NEXT_PUBLIC_DAT_URL}/${url}`); + const dataFromKey = key ? data[key] : data?.table_data || data?.chart_data || data; + setResponseData(dataFromKey) + } catch (e: any) { + console.error(e); + } finally { + setLoading(false); + } + }, [key, url]); useEffect(() => { init(); }, [init, url]); useEffect(() => { - if (dontRefetch) return; - init(); - }, [dataContext.dates.from, dataContext.dates.to, dontRefetch, init]); + if(!responseData) return; + + setData( + responseData.filter + ? responseData.filter((line: any) => { + if (!line.time) { + return true; + } + if ( + dataContext.dates.from && + new Date(line.time) < new Date(dataContext.dates.from) + ) { + return false; + } + if (dataContext.dates.to && new Date(line.time) > new Date(dataContext.dates.to)) { + return false; + } + return true; + }) + : responseData + ); + }, [dataContext.dates.from, dataContext.dates.to, responseData]) return [data, loading]; }