Commit c0239eef authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #1482 from blockscout/tom2drum/issue-1405

Gas tooltip: periodical updates of data
parents d71e398d 18b7f4b7
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import _omit from 'lodash/omit';
import _pickBy from 'lodash/pickBy'; import _pickBy from 'lodash/pickBy';
import React from 'react'; import React from 'react';
...@@ -19,7 +20,7 @@ import type { ApiResource, ResourceName, ResourcePathParams } from './resources' ...@@ -19,7 +20,7 @@ import type { ApiResource, ResourceName, ResourcePathParams } from './resources'
export interface Params<R extends ResourceName> { export interface Params<R extends ResourceName> {
pathParams?: ResourcePathParams<R>; pathParams?: ResourcePathParams<R>;
queryParams?: Record<string, string | Array<string> | number | undefined>; queryParams?: Record<string, string | Array<string> | number | undefined>;
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal'>; fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal' | 'headers'>;
} }
export default function useApiFetch() { export default function useApiFetch() {
...@@ -40,6 +41,7 @@ export default function useApiFetch() { ...@@ -40,6 +41,7 @@ export default function useApiFetch() {
'x-endpoint': resource.endpoint && isNeedProxy() ? resource.endpoint : undefined, 'x-endpoint': resource.endpoint && isNeedProxy() ? resource.endpoint : undefined,
Authorization: resource.endpoint && resource.needAuth ? apiToken : undefined, Authorization: resource.endpoint && resource.needAuth ? apiToken : undefined,
'x-csrf-token': withBody && csrfToken ? csrfToken : undefined, 'x-csrf-token': withBody && csrfToken ? csrfToken : undefined,
...fetchParams?.headers,
}, Boolean) as HeadersInit; }, Boolean) as HeadersInit;
return fetch<SuccessType, ErrorType>( return fetch<SuccessType, ErrorType>(
...@@ -51,7 +53,7 @@ export default function useApiFetch() { ...@@ -51,7 +53,7 @@ export default function useApiFetch() {
// change condition here if something is changed // change condition here if something is changed
credentials: config.features.account.isEnabled ? 'include' : 'same-origin', credentials: config.features.account.isEnabled ? 'include' : 'same-origin',
headers, headers,
...fetchParams, ..._omit(fetchParams, 'headers'),
}, },
{ {
resource: resource.path, resource: resource.path,
......
...@@ -3,11 +3,26 @@ import type { HomeStats } from 'types/api/stats'; ...@@ -3,11 +3,26 @@ import type { HomeStats } from 'types/api/stats';
export const base: HomeStats = { export const base: HomeStats = {
average_block_time: 6212.0, average_block_time: 6212.0,
coin_price: '0.00199678', coin_price: '0.00199678',
coin_price_change_percentage: -7.42,
gas_prices: { gas_prices: {
average: 48.0, average: {
fast: 67.5, fiat_price: '1.01',
slow: 48.0, price: 20.41,
time: 12283,
},
fast: {
fiat_price: '1.26',
price: 25.47,
time: 9321,
},
slow: {
fiat_price: '0.97',
price: 19.55,
time: 24543,
},
}, },
gas_price_updated_at: '2022-11-11T11:09:49.051171Z',
gas_prices_update_in: 300000,
gas_used_today: '4108680603', gas_used_today: '4108680603',
market_cap: '330809.96443288102524', market_cap: '330809.96443288102524',
network_utilization_percentage: 1.55372064, network_utilization_percentage: 1.55372064,
......
import type { IncomingMessage } from 'http'; import type { IncomingMessage } from 'http';
import _pick from 'lodash/pick';
import type { NextApiRequest } from 'next'; import type { NextApiRequest } from 'next';
import type { NextApiRequestCookies } from 'next/dist/server/api-utils'; import type { NextApiRequestCookies } from 'next/dist/server/api-utils';
import type { RequestInit, Response } from 'node-fetch'; import type { RequestInit, Response } from 'node-fetch';
...@@ -14,16 +15,18 @@ export default function fetchFactory( ...@@ -14,16 +15,18 @@ export default function fetchFactory(
// first arg can be only a string // first arg can be only a string
// FIXME migrate to RequestInfo later if needed // FIXME migrate to RequestInfo later if needed
return function fetch(url: string, init?: RequestInit): Promise<Response> { return function fetch(url: string, init?: RequestInit): Promise<Response> {
const csrfToken = _req.headers['x-csrf-token'];
const authToken = _req.headers['Authorization'];
const apiToken = _req.cookies[cookies.NAMES.API_TOKEN]; const apiToken = _req.cookies[cookies.NAMES.API_TOKEN];
const headers = { const headers = {
accept: _req.headers['accept'] || 'application/json', accept: _req.headers['accept'] || 'application/json',
'content-type': _req.headers['content-type'] || 'application/json', 'content-type': _req.headers['content-type'] || 'application/json',
cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '', cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '',
...(csrfToken ? { 'x-csrf-token': String(csrfToken) } : {}), ..._pick(_req.headers, [
...(authToken ? { Authorization: String(authToken) } : {}), 'x-csrf-token',
'Authorization',
// feature flags
'updated-gas-oracle',
]) as Record<string, string | undefined>,
}; };
httpLogger.logger.info({ httpLogger.logger.info({
......
...@@ -3,11 +3,26 @@ import type { Counter, HomeStats, StatsChartsSection } from 'types/api/stats'; ...@@ -3,11 +3,26 @@ import type { Counter, HomeStats, StatsChartsSection } from 'types/api/stats';
export const HOMEPAGE_STATS: HomeStats = { export const HOMEPAGE_STATS: HomeStats = {
average_block_time: 14346, average_block_time: 14346,
coin_price: '1807.68', coin_price: '1807.68',
coin_price_change_percentage: 42,
gas_prices: { gas_prices: {
average: 0.1, average: {
fast: 0.11, fiat_price: '1.01',
slow: 0.1, price: 20.41,
time: 12283,
},
fast: {
fiat_price: '1.26',
price: 25.47,
time: 9321,
},
slow: {
fiat_price: '0.97',
price: 19.55,
time: 24543,
},
}, },
gas_price_updated_at: '2022-11-11T11:09:49.051171Z',
gas_prices_update_in: 300000,
gas_used_today: '0', gas_used_today: '0',
market_cap: '0', market_cap: '0',
network_utilization_percentage: 22.56, network_utilization_percentage: 22.56,
......
...@@ -4,10 +4,13 @@ export type HomeStats = { ...@@ -4,10 +4,13 @@ export type HomeStats = {
total_transactions: string; total_transactions: string;
average_block_time: number; average_block_time: number;
coin_price: string | null; coin_price: string | null;
coin_price_change_percentage: number | null; // e.g -6.22
total_gas_used: string; total_gas_used: string;
transactions_today: string; transactions_today: string;
gas_used_today: string; gas_used_today: string;
gas_prices: GasPrices | null; gas_prices: GasPrices | null;
gas_price_updated_at: string | null;
gas_prices_update_in: number;
static_gas_price: string | null; static_gas_price: string | null;
market_cap: string; market_cap: string;
network_utilization_percentage: number; network_utilization_percentage: number;
...@@ -16,9 +19,15 @@ export type HomeStats = { ...@@ -16,9 +19,15 @@ export type HomeStats = {
} }
export type GasPrices = { export type GasPrices = {
average: number | null; average: GasPriceInfo | null;
fast: number | null; fast: GasPriceInfo | null;
slow: number | null; slow: GasPriceInfo | null;
}
export interface GasPriceInfo {
fiat_price: string | null;
price: number | null;
time: number | null;
} }
export type Counters = { export type Counters = {
......
...@@ -14,6 +14,11 @@ interface Props { ...@@ -14,6 +14,11 @@ interface Props {
const BlocksTabSlot = ({ pagination }: Props) => { const BlocksTabSlot = ({ pagination }: Props) => {
const statsQuery = useApiQuery('homepage_stats', { const statsQuery = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: { queryOptions: {
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
}, },
......
...@@ -37,7 +37,13 @@ const LatestBlocks = () => { ...@@ -37,7 +37,13 @@ const LatestBlocks = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const statsQueryResult = useApiQuery('homepage_stats', { const statsQueryResult = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: { queryOptions: {
refetchOnMount: false,
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
}, },
}); });
......
...@@ -16,8 +16,14 @@ const hasGasTracker = config.UI.homepage.showGasTracker; ...@@ -16,8 +16,14 @@ const hasGasTracker = config.UI.homepage.showGasTracker;
const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime; const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
const Stats = () => { const Stats = () => {
const { data, isPlaceholderData, isError } = useApiQuery('homepage_stats', { const { data, isPlaceholderData, isError, dataUpdatedAt } = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: { queryOptions: {
refetchOnMount: false,
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
}, },
}); });
...@@ -45,7 +51,19 @@ const Stats = () => { ...@@ -45,7 +51,19 @@ const Stats = () => {
!data.gas_prices && itemsCount--; !data.gas_prices && itemsCount--;
data.rootstock_locked_btc && itemsCount++; data.rootstock_locked_btc && itemsCount++;
const isOdd = Boolean(itemsCount % 2); const isOdd = Boolean(itemsCount % 2);
const gasLabel = hasGasTracker && data.gas_prices ? <GasInfoTooltipContent gasPrices={ data.gas_prices }/> : null; const gasLabel = hasGasTracker && data.gas_prices ? <GasInfoTooltipContent data={ data } dataUpdatedAt={ dataUpdatedAt }/> : null;
const gasPriceText = (() => {
if (data.gas_prices?.average?.fiat_price) {
return `$${ data.gas_prices.average.fiat_price }`;
}
if (data.gas_prices?.average?.price) {
return `${ data.gas_prices.average.price.toLocaleString() } Gwei`;
}
return 'N/A';
})();
content = ( content = (
<> <>
...@@ -92,7 +110,7 @@ const Stats = () => { ...@@ -92,7 +110,7 @@ const Stats = () => {
<StatsItem <StatsItem
icon="gas" icon="gas"
title="Gas tracker" title="Gas tracker"
value={ data.gas_prices.average !== null ? `${ Number(data.gas_prices.average).toLocaleString() } Gwei` : 'N/A' } value={ gasPriceText }
_last={ isOdd ? lastItemTouchStyle : undefined } _last={ isOdd ? lastItemTouchStyle : undefined }
tooltipLabel={ gasLabel } tooltipLabel={ gasLabel }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
......
...@@ -3,6 +3,7 @@ import React from 'react'; ...@@ -3,6 +3,7 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { HOMEPAGE_STATS } from 'stubs/stats';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
import ChainIndicatorChartContainer from './ChainIndicatorChartContainer'; import ChainIndicatorChartContainer from './ChainIndicatorChartContainer';
...@@ -29,7 +30,17 @@ const ChainIndicators = () => { ...@@ -29,7 +30,17 @@ const ChainIndicators = () => {
const indicator = indicators.find(({ id }) => id === selectedIndicator); const indicator = indicators.find(({ id }) => id === selectedIndicator);
const queryResult = useFetchChartData(indicator); const queryResult = useFetchChartData(indicator);
const statsQueryResult = useApiQuery('homepage_stats'); const statsQueryResult = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: {
refetchOnMount: false,
placeholderData: HOMEPAGE_STATS,
},
});
const bgColorDesktop = useColorModeValue('white', 'gray.900'); const bgColorDesktop = useColorModeValue('white', 'gray.900');
const bgColorMobile = useColorModeValue('white', 'black'); const bgColorMobile = useColorModeValue('white', 'black');
......
import { GridItem, chakra } from '@chakra-ui/react';
import React from 'react';
import type { GasPriceInfo } from 'types/api/stats';
import { asymp, space } from 'lib/html-entities';
interface Props {
name: string;
info: GasPriceInfo | null;
}
const GasInfoRow = ({ name, info }: Props) => {
const content = (() => {
if (!info || info.price === null) {
return 'N/A';
}
return (
<>
<span>{ info.fiat_price ? `$${ info.fiat_price }` : `${ info.price } Gwei` }</span>
{ info.time && (
<chakra.span color="text_secondary">
{ space }per tx { asymp } { (info.time / 1000).toLocaleString(undefined, { maximumFractionDigits: 1 }) }s
</chakra.span>
) }
</>
);
})();
return (
<>
<GridItem color="blue.100">{ name }</GridItem>
<GridItem color="text" textAlign="right">{ content }</GridItem>
</>
);
};
export default React.memo(GasInfoRow);
import { Grid, GridItem, useColorModeValue } from '@chakra-ui/react'; import { DarkMode, Grid, GridItem } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { GasPrices } from 'types/api/stats'; import type { HomeStats } from 'types/api/stats';
const GasInfoTooltipContent = ({ gasPrices }: {gasPrices: GasPrices}) => { import dayjs from 'lib/date/dayjs';
const nameStyleProps = {
color: useColorModeValue('blue.100', 'blue.600'), import GasInfoRow from './GasInfoRow';
}; import GasInfoUpdateTimer from './GasInfoUpdateTimer';
interface Props {
data: HomeStats;
dataUpdatedAt: number;
}
const GasInfoTooltipContent = ({ data, dataUpdatedAt }: Props) => {
if (!data.gas_prices) {
return null;
}
return ( return (
<Grid templateColumns="repeat(2, max-content)" rowGap={ 2 } columnGap={ 4 } padding={ 4 } fontSize="xs"> <DarkMode>
<GridItem { ...nameStyleProps }>Slow</GridItem> <Grid templateColumns="repeat(2, max-content)" rowGap={ 2 } columnGap={ 4 } padding={ 4 } fontSize="xs" lineHeight={ 4 }>
<GridItem>{ gasPrices.slow !== null ? `${ gasPrices.slow } Gwei` : 'N/A' }</GridItem> { data.gas_price_updated_at && (
<GridItem { ...nameStyleProps }>Average</GridItem> <>
<GridItem>{ gasPrices.average !== null ? `${ gasPrices.average } Gwei` : 'N/A' }</GridItem> <GridItem color="text_secondary">Last update</GridItem>
<GridItem { ...nameStyleProps }>Fast</GridItem> <GridItem color="text_secondary" display="flex" justifyContent="flex-end" columnGap={ 2 }>
<GridItem>{ gasPrices.fast !== null ? `${ gasPrices.fast } Gwei` : 'N/A' }</GridItem> { dayjs(data.gas_price_updated_at).format('MMM DD, HH:mm:ss') }
</Grid> { data.gas_prices_update_in !== 0 &&
<GasInfoUpdateTimer key={ dataUpdatedAt } startTime={ dataUpdatedAt } duration={ data.gas_prices_update_in }/> }
</GridItem>
</>
) }
<GasInfoRow name="Slow" info={ data.gas_prices.slow }/>
<GasInfoRow name="Normal" info={ data.gas_prices.average }/>
<GasInfoRow name="Fast" info={ data.gas_prices.fast }/>
</Grid>
</DarkMode>
); );
}; };
......
import { CircularProgress } from '@chakra-ui/react';
import React from 'react';
import dayjs from 'lib/date/dayjs';
interface Props {
startTime: number;
duration: number;
}
const getValue = (startDate: dayjs.Dayjs, duration: number) => {
const now = dayjs();
const diff = now.diff(startDate, 'ms');
const value = diff / duration * 100;
if (value >= 99) {
return 99;
}
return value;
};
const GasInfoUpdateTimer = ({ startTime, duration }: Props) => {
const [ value, setValue ] = React.useState(getValue(dayjs(startTime), duration));
React.useEffect(() => {
const startDate = dayjs(startTime);
const intervalId = window.setInterval(() => {
const nextValue = getValue(startDate, duration);
setValue(nextValue);
if (nextValue === 99) {
window.clearInterval(intervalId);
}
}, 100);
return () => {
window.clearInterval(intervalId);
};
}, [ startTime, duration ]);
return <CircularProgress value={ value } trackColor="whiteAlpha.100" size={ 4 }/>;
};
export default React.memo(GasInfoUpdateTimer);
...@@ -19,7 +19,7 @@ test('default view +@dark-mode +@mobile', async({ mount, page }) => { ...@@ -19,7 +19,7 @@ test('default view +@dark-mode +@mobile', async({ mount, page }) => {
</TestApp>, </TestApp>,
); );
await component.getByText(/gwei/i).hover(); await component.getByText(/\$1\.01/).hover();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } }); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } });
await component.getByLabel('color mode switch').click(); await component.getByLabel('color mode switch').click();
......
import { Flex, LightMode, Link, Skeleton, Tooltip, chakra, useDisclosure } from '@chakra-ui/react'; import { Flex, Link, Skeleton, Tooltip, chakra, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import GasInfoTooltipContent from 'ui/shared/GasInfoTooltipContent/GasInfoTooltipContent'; import GasInfoTooltipContent from 'ui/shared/GasInfoTooltipContent/GasInfoTooltipContent';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
...@@ -16,13 +17,39 @@ const TopBarStats = () => { ...@@ -16,13 +17,39 @@ const TopBarStats = () => {
onToggle(); onToggle();
}, [ onToggle ]); }, [ onToggle ]);
const { data, isPlaceholderData, isError } = useApiQuery('homepage_stats', { const { data, isPlaceholderData, isError, refetch, dataUpdatedAt } = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: { queryOptions: {
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
refetchOnMount: false, refetchOnMount: false,
}, },
}); });
React.useEffect(() => {
if (isPlaceholderData || !data?.gas_price_updated_at) {
return;
}
const endDate = dayjs(dataUpdatedAt).add(data.gas_prices_update_in, 'ms');
const timeout = endDate.diff(dayjs(), 'ms');
if (timeout <= 0) {
return;
}
const timeoutId = window.setTimeout(() => {
refetch();
}, timeout);
return () => {
window.clearTimeout(timeoutId);
};
}, [ isPlaceholderData, data?.gas_price_updated_at, dataUpdatedAt, data?.gas_prices_update_in, refetch ]);
if (isError) { if (isError) {
return <div/>; return <div/>;
} }
...@@ -34,35 +61,42 @@ const TopBarStats = () => { ...@@ -34,35 +61,42 @@ const TopBarStats = () => {
fontWeight={ 500 } fontWeight={ 500 }
> >
{ data?.coin_price && ( { data?.coin_price && (
<Skeleton isLoaded={ !isPlaceholderData }> <Flex columnGap={ 1 }>
<chakra.span color="text_secondary">{ config.chain.governanceToken.symbol || config.chain.currency.symbol } </chakra.span> <Skeleton isLoaded={ !isPlaceholderData }>
<span>${ Number(data.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }) }</span> <chakra.span color="text_secondary">{ config.chain.governanceToken.symbol || config.chain.currency.symbol } </chakra.span>
</Skeleton> <span>${ Number(data.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }) }</span>
</Skeleton>
{ data.coin_price_change_percentage && (
<Skeleton isLoaded={ !isPlaceholderData }>
<chakra.span color={ Number(data.coin_price_change_percentage) >= 0 ? 'green.500' : 'red.500' }>
{ Number(data.coin_price_change_percentage).toFixed(2) }%
</chakra.span>
</Skeleton>
) }
</Flex>
) } ) }
{ data?.coin_price && config.UI.homepage.showGasTracker && <TextSeparator color="divider"/> } { data?.coin_price && config.UI.homepage.showGasTracker && <TextSeparator color="divider"/> }
{ data?.gas_prices && data.gas_prices.average !== null && config.UI.homepage.showGasTracker && ( { data?.gas_prices && data.gas_prices.average !== null && config.UI.homepage.showGasTracker && (
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
<chakra.span color="text_secondary">Gas </chakra.span> <chakra.span color="text_secondary">Gas </chakra.span>
<LightMode> <Tooltip
<Tooltip label={ <GasInfoTooltipContent data={ data } dataUpdatedAt={ dataUpdatedAt }/> }
label={ <GasInfoTooltipContent gasPrices={ data.gas_prices }/> } hasArrow={ false }
hasArrow={ false } borderRadius="md"
borderRadius="md" offset={ [ 0, 16 ] }
offset={ [ 0, 16 ] } bgColor="blackAlpha.900"
bgColor="blackAlpha.900" p={ 0 }
p={ 0 } isOpen={ isOpen }
isOpen={ isOpen } >
<Link
_hover={{ textDecoration: 'none', color: 'link_hovered' }}
onClick={ handleClick }
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
> >
<Link { data.gas_prices.average.fiat_price ? `$${ data.gas_prices.average.fiat_price }` : `${ data.gas_prices.average.price } Gwei` }
_hover={{ textDecoration: 'none', color: 'link_hovered' }} </Link>
onClick={ handleClick } </Tooltip>
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
>
{ data.gas_prices.average } Gwei
</Link>
</Tooltip>
</LightMode>
</Skeleton> </Skeleton>
) } ) }
</Flex> </Flex>
......
import type { TooltipProps } from '@chakra-ui/react';
import { Box, Grid, Heading, List, ListItem, Skeleton } from '@chakra-ui/react'; import { Box, Grid, Heading, List, ListItem, Skeleton } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { StatsChartsSection } from 'types/api/stats'; import type { StatsChartsSection } from 'types/api/stats';
import type { StatsIntervalIds } from 'types/client/stats'; import type { StatsIntervalIds } from 'types/client/stats';
import useApiQuery from 'lib/api/useApiQuery';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import EmptySearchResult from 'ui/shared/EmptySearchResult'; import EmptySearchResult from 'ui/shared/EmptySearchResult';
import GasInfoTooltipContent from 'ui/shared/GasInfoTooltipContent/GasInfoTooltipContent';
import Hint from 'ui/shared/Hint';
import ChartsLoadingErrorAlert from './ChartsLoadingErrorAlert'; import ChartsLoadingErrorAlert from './ChartsLoadingErrorAlert';
import ChartWidgetContainer from './ChartWidgetContainer'; import ChartWidgetContainer from './ChartWidgetContainer';
const GAS_TOOLTIP_PROPS: Partial<TooltipProps> = {
borderRadius: 'md',
hasArrow: false,
padding: 0,
};
type Props = { type Props = {
filterQuery: string; filterQuery: string;
isError: boolean; isError: boolean;
...@@ -23,6 +33,17 @@ const ChartsWidgetsList = ({ filterQuery, isError, isPlaceholderData, charts, in ...@@ -23,6 +33,17 @@ const ChartsWidgetsList = ({ filterQuery, isError, isPlaceholderData, charts, in
const isAnyChartDisplayed = charts?.some((section) => section.charts.length > 0); const isAnyChartDisplayed = charts?.some((section) => section.charts.length > 0);
const isEmptyChartList = Boolean(filterQuery) && !isAnyChartDisplayed; const isEmptyChartList = Boolean(filterQuery) && !isAnyChartDisplayed;
const homeStatsQuery = useApiQuery('homepage_stats', {
fetchParams: {
headers: {
'updated-gas-oracle': 'true',
},
},
queryOptions: {
refetchOnMount: false,
},
});
const handleChartLoadingError = useCallback( const handleChartLoadingError = useCallback(
() => setIsSomeChartLoadingError(true), () => setIsSomeChartLoadingError(true),
[ setIsSomeChartLoadingError ]); [ setIsSomeChartLoadingError ]);
...@@ -51,10 +72,16 @@ const ChartsWidgetsList = ({ filterQuery, isError, isPlaceholderData, charts, in ...@@ -51,10 +72,16 @@ const ChartsWidgetsList = ({ filterQuery, isError, isPlaceholderData, charts, in
marginBottom: 0, marginBottom: 0,
}} }}
> >
<Skeleton isLoaded={ !isPlaceholderData } mb={ 4 } display="inline-block"> <Skeleton isLoaded={ !isPlaceholderData } mb={ 4 } display="inline-flex" alignItems="center" columnGap={ 2 }>
<Heading size="md" > <Heading size="md" >
{ section.title } { section.title }
</Heading> </Heading>
{ section.id === 'gas' && homeStatsQuery.data && homeStatsQuery.data.gas_prices && (
<Hint
label={ <GasInfoTooltipContent data={ homeStatsQuery.data } dataUpdatedAt={ homeStatsQuery.dataUpdatedAt }/> }
tooltipProps={ GAS_TOOLTIP_PROPS }
/>
) }
</Skeleton> </Skeleton>
<Grid <Grid
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment