Commit 71fd48cd authored by tom goriunov's avatar tom goriunov Committed by GitHub

Home page stats config (#2221)

Fixes #2143
parent a3cdf628
import type { ContractCodeIde } from 'types/client/contract'; import type { ContractCodeIde } from 'types/client/contract';
import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId, type NavigationLayout } from 'types/client/navigation'; import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId, type NavigationLayout } from 'types/client/navigation';
import type { ChainIndicatorId, HeroBannerConfig } from 'types/homepage'; import { HOME_STATS_WIDGET_IDS, type ChainIndicatorId, type HeroBannerConfig, type HomeStatsWidgetId } from 'types/homepage';
import type { NetworkExplorer } from 'types/networks'; import type { NetworkExplorer } from 'types/networks';
import type { ColorThemeId } from 'types/settings'; import type { ColorThemeId } from 'types/settings';
import type { FontFamily } from 'types/ui'; import type { FontFamily } from 'types/ui';
import { COLOR_THEMES } from 'lib/settings/colorTheme'; import { COLOR_THEMES } from 'lib/settings/colorTheme';
import * as features from './features';
import * as views from './ui/views'; import * as views from './ui/views';
import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils';
...@@ -25,6 +26,22 @@ const hiddenLinks = (() => { ...@@ -25,6 +26,22 @@ const hiddenLinks = (() => {
return result; return result;
})(); })();
const homePageStats: Array<HomeStatsWidgetId> = (() => {
const parsedValue = parseEnvJson<Array<HomeStatsWidgetId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_STATS'));
if (!Array.isArray(parsedValue)) {
const rollupFeature = features.rollup;
if (rollupFeature.isEnabled && [ 'zkEvm', 'zkSync', 'arbitrum' ].includes(rollupFeature.type)) {
return [ 'latest_batch', 'average_block_time', 'total_txs', 'wallet_addresses', 'gas_tracker' ];
}
return [ 'total_blocks', 'average_block_time', 'total_txs', 'wallet_addresses', 'gas_tracker' ];
}
return parsedValue.filter((item) => HOME_STATS_WIDGET_IDS.includes(item));
})();
const highlightedRoutes = (() => { const highlightedRoutes = (() => {
const parsedValue = parseEnvJson<Array<NavigationLinkId>>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES')); const parsedValue = parseEnvJson<Array<NavigationLinkId>>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES'));
return Array.isArray(parsedValue) ? parsedValue : []; return Array.isArray(parsedValue) ? parsedValue : [];
...@@ -58,12 +75,13 @@ const UI = Object.freeze({ ...@@ -58,12 +75,13 @@ const UI = Object.freeze({
}, },
homepage: { homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [], charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [],
stats: homePageStats,
heroBanner: parseEnvJson<HeroBannerConfig>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG')), heroBanner: parseEnvJson<HeroBannerConfig>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG')),
// !!! DEPRECATED !!!
plate: { plate: {
background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND'), background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND'),
textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR'), textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR'),
}, },
showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true,
}, },
views, views,
indexingAlert: { indexingAlert: {
......
...@@ -36,4 +36,5 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true ...@@ -36,4 +36,5 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch']
\ No newline at end of file
...@@ -24,7 +24,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ ...@@ -24,7 +24,6 @@ NEXT_PUBLIC_API_BASE_PATH=/
# ui config # ui config
## homepage ## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
## sidebar ## sidebar
NEXT_PUBLIC_NETWORK_LOGO= NEXT_PUBLIC_NETWORK_LOGO=
......
...@@ -25,7 +25,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ ...@@ -25,7 +25,6 @@ NEXT_PUBLIC_API_BASE_PATH=/
# ui config # ui config
## homepage ## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
## sidebar ## sidebar
## footer ## footer
NEXT_PUBLIC_GIT_TAG=v1.0.11 NEXT_PUBLIC_GIT_TAG=v1.0.11
......
...@@ -42,4 +42,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com ...@@ -42,4 +42,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce'] NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','btc_locked']
\ No newline at end of file
...@@ -29,8 +29,8 @@ import type { ValidatorsChainType } from '../../../types/client/validators'; ...@@ -29,8 +29,8 @@ import type { ValidatorsChainType } from '../../../types/client/validators';
import type { WalletType } from '../../../types/client/wallets'; import type { WalletType } from '../../../types/client/wallets';
import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import { SUPPORTED_WALLETS } from '../../../types/client/wallets';
import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks';
import { CHAIN_INDICATOR_IDS } from '../../../types/homepage'; import { CHAIN_INDICATOR_IDS, HOME_STATS_WIDGET_IDS } from '../../../types/homepage';
import type { ChainIndicatorId, HeroBannerButtonState, HeroBannerConfig } from '../../../types/homepage'; import type { ChainIndicatorId, HeroBannerButtonState, HeroBannerConfig, HomeStatsWidgetId } from '../../../types/homepage';
import { type NetworkVerificationTypeEnvs, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks'; import { type NetworkVerificationTypeEnvs, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks';
import { COLOR_THEME_IDS } from '../../../types/settings'; import { COLOR_THEME_IDS } from '../../../types/settings';
import type { FontFamily } from '../../../types/ui'; import type { FontFamily } from '../../../types/ui';
...@@ -567,6 +567,11 @@ const schema = yup ...@@ -567,6 +567,11 @@ const schema = yup
.transform(replaceQuotes) .transform(replaceQuotes)
.json() .json()
.of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS)), .of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS)),
NEXT_PUBLIC_HOMEPAGE_STATS: yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<HomeStatsWidgetId>().oneOf(HOME_STATS_WIDGET_IDS)),
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(),
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(),
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup
...@@ -586,7 +591,6 @@ const schema = yup ...@@ -586,7 +591,6 @@ const schema = yup
const isUndefined = data === undefined; const isUndefined = data === undefined;
return isUndefined || heroBannerSchema.isValidSync(data); return isUndefined || heroBannerSchema.isValidSync(data);
}), }),
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME: yup.boolean(),
// b. sidebar // b. sidebar
NEXT_PUBLIC_FEATURED_NETWORKS: yup NEXT_PUBLIC_FEATURED_NETWORKS: yup
......
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none
NEXT_PUBLIC_API_SPEC_URL=none NEXT_PUBLIC_API_SPEC_URL=none
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none
\ No newline at end of file NEXT_PUBLIC_HOMEPAGE_STATS=[]
\ No newline at end of file
...@@ -35,10 +35,10 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c18099 ...@@ -35,10 +35,10 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c18099
NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false
NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch']
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='#fff' NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='#fff'
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgb(255, 145, 0)' NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgb(255, 145, 0)'
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['lightpink'],'text_color':['deepskyblue','white'],'border':['3px solid black']} NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['lightpink'],'text_color':['deepskyblue','white'],'border':['3px solid black']}
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_GAS_TRACKER_ENABLED=true NEXT_PUBLIC_GAS_TRACKER_ENABLED=true
NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei'] NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei']
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
......
...@@ -10,3 +10,4 @@ ...@@ -10,3 +10,4 @@
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | - | v1.25.0 | Replaced by NEXT_PUBLIC_GAS_TRACKER_ENABLED | | NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | - | v1.25.0 | Replaced by NEXT_PUBLIC_GAS_TRACKER_ENABLED |
| NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.12.0 | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL | | NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.12.0 | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL |
| NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` | v1.24.0 | v1.31.0 | Replaced by NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS | | NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` | v1.24.0 | v1.31.0 | Replaced by NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | v1.35.0 | Replaces by NEXT_PUBLIC_HOMEPAGE_STATS
\ No newline at end of file
...@@ -118,9 +118,9 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -118,9 +118,9 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ | | NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_txs' \| 'latest_l1_state_batch' \| 'wallet_addresses' \| 'gas_tracker' \| 'btc_locked' \| 'current_epoch'>` | List of stats widgets displayed on the home page | - | For zkSync, zkEvm and Arbitrum rollups: `['latest_batch','average_block_time','total_txs','wallet_addresses','gas_tracker']`, for other cases: `['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker']` | `['total_blocks','total_txs','wallet_addresses']` | v1.35.x+ |
| NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `white` | `\#DCFE76` | v1.0.x+ | | NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `white` | `\#DCFE76` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ | | NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG | `HeroBannerConfig`, see details [below](#hero-banner-configuration-properties) | Configuration of hero banner appearance. | - | - | See [below](#hero-banner-configuration-properties) | v1.35.0+ | | NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG | `HeroBannerConfig`, see details [below](#hero-banner-configuration-properties) | Configuration of hero banner appearance. | - | - | See [below](#hero-banner-configuration-properties) | v1.35.0+ |
#### Hero banner configuration properties #### Hero banner configuration properties
......
export const CHAIN_INDICATOR_IDS = [ 'daily_txs', 'coin_price', 'secondary_coin_price', 'market_cap', 'tvl' ] as const; export const CHAIN_INDICATOR_IDS = [ 'daily_txs', 'coin_price', 'secondary_coin_price', 'market_cap', 'tvl' ] as const;
export type ChainIndicatorId = typeof CHAIN_INDICATOR_IDS[number]; export type ChainIndicatorId = typeof CHAIN_INDICATOR_IDS[number];
export const HOME_STATS_WIDGET_IDS = [
'latest_batch',
'total_blocks',
'average_block_time',
'total_txs',
'latest_l1_state_batch',
'wallet_addresses',
'gas_tracker',
'btc_locked',
'current_epoch',
] as const;
export type HomeStatsWidgetId = typeof HOME_STATS_WIDGET_IDS[number];
export interface HeroBannerButtonState { export interface HeroBannerButtonState {
background?: Array<string | undefined>; background?: Array<string | undefined>;
text_color?: Array<string | undefined>; text_color?: Array<string | undefined>;
......
...@@ -9,7 +9,10 @@ import Stats from './Stats'; ...@@ -9,7 +9,10 @@ import Stats from './Stats';
test.describe('all items', () => { test.describe('all items', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ render, mockApiResponse }) => { test.beforeEach(async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_STATS', '["total_blocks","average_block_time","total_txs","wallet_addresses","gas_tracker","btc_locked"]' ],
]);
await mockApiResponse('stats', statsMock.withBtcLocked); await mockApiResponse('stats', statsMock.withBtcLocked);
component = await render(<Stats/>); component = await render(<Stats/>);
}); });
...@@ -28,7 +31,7 @@ test('no gas info', async({ render, mockApiResponse }) => { ...@@ -28,7 +31,7 @@ test('no gas info', async({ render, mockApiResponse }) => {
test('4 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => { test('4 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ], [ 'NEXT_PUBLIC_HOMEPAGE_STATS', '["total_txs","gas_tracker","wallet_addresses","total_blocks"]' ],
]); ]);
await mockApiResponse('stats', statsMock.base); await mockApiResponse('stats', statsMock.base);
const component = await render(<Stats/>); const component = await render(<Stats/>);
...@@ -37,8 +40,7 @@ test('4 items default view +@mobile -@default', async({ render, mockApiResponse, ...@@ -37,8 +40,7 @@ test('4 items default view +@mobile -@default', async({ render, mockApiResponse,
test('3 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => { test('3 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ], [ 'NEXT_PUBLIC_HOMEPAGE_STATS', '["total_txs","wallet_addresses","total_blocks"]' ],
[ 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', 'false' ],
]); ]);
await mockApiResponse('stats', statsMock.base); await mockApiResponse('stats', statsMock.base);
const component = await render(<Stats/>); const component = await render(<Stats/>);
......
...@@ -2,6 +2,8 @@ import { Grid } from '@chakra-ui/react'; ...@@ -2,6 +2,8 @@ import { Grid } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { HomeStatsWidgetId } from 'types/homepage';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
...@@ -12,7 +14,6 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -12,7 +14,6 @@ import IconSvg from 'ui/shared/IconSvg';
import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget'; import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget';
import StatsWidget from 'ui/shared/stats/StatsWidget'; import StatsWidget from 'ui/shared/stats/StatsWidget';
const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
const Stats = () => { const Stats = () => {
...@@ -35,37 +36,54 @@ const Stats = () => { ...@@ -35,37 +36,54 @@ const Stats = () => {
const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', { const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', {
queryOptions: { queryOptions: {
placeholderData: 12345, placeholderData: 12345,
enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm', enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' && config.UI.homepage.stats.includes('latest_batch'),
}, },
}); });
const zkSyncLatestBatchQuery = useApiQuery('homepage_zksync_latest_batch', { const zkSyncLatestBatchQuery = useApiQuery('homepage_zksync_latest_batch', {
queryOptions: { queryOptions: {
placeholderData: 12345, placeholderData: 12345,
enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkSync', enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && config.UI.homepage.stats.includes('latest_batch'),
}, },
}); });
const arbitrumLatestBatchQuery = useApiQuery('homepage_arbitrum_latest_batch', { const arbitrumLatestBatchQuery = useApiQuery('homepage_arbitrum_latest_batch', {
queryOptions: { queryOptions: {
placeholderData: 12345, placeholderData: 12345,
enabled: rollupFeature.isEnabled && rollupFeature.type === 'arbitrum', enabled: rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && config.UI.homepage.stats.includes('latest_batch'),
}, },
}); });
if (isError || zkEvmLatestBatchQuery.isError || zkSyncLatestBatchQuery.isError || arbitrumLatestBatchQuery.isError) { const latestBatchQuery = (() => {
if (!rollupFeature.isEnabled || !config.UI.homepage.stats.includes('latest_batch')) {
return;
}
switch (rollupFeature.type) {
case 'zkEvm':
return zkEvmLatestBatchQuery;
case 'zkSync':
return zkSyncLatestBatchQuery;
case 'arbitrum':
return arbitrumLatestBatchQuery;
}
})();
if (isError || latestBatchQuery?.isError) {
return null; return null;
} }
const isLoading = isPlaceholderData || const isLoading = isPlaceholderData || latestBatchQuery?.isPlaceholderData;
(rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' && zkEvmLatestBatchQuery.isPlaceholderData) ||
(rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && zkSyncLatestBatchQuery.isPlaceholderData) || interface Item extends StatsWidgetProps {
(rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && arbitrumLatestBatchQuery.isPlaceholderData); id: HomeStatsWidgetId;
}
const content = (() => { const items: Array<Item> = (() => {
if (!data) { if (!data) {
return null; return [];
} }
const gasInfoTooltip = hasGasTracker && data.gas_prices && data.gas_prices.average ? ( const gasInfoTooltip = hasGasTracker && data.gas_prices && data.gas_prices.average ? (
<GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt }> <GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt }>
<IconSvg <IconSvg
...@@ -80,41 +98,40 @@ const Stats = () => { ...@@ -80,41 +98,40 @@ const Stats = () => {
</GasInfoTooltip> </GasInfoTooltip>
) : null; ) : null;
const hasBatches = rollupFeature.isEnabled && (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum'); return [
const latestBatch = latestBatchQuery?.data !== undefined && {
(hasBatches && rollupFeature.type === 'zkEvm' ? zkEvmLatestBatchQuery.data : null) || id: 'latest_batch' as const,
(hasBatches && rollupFeature.type === 'zkSync' ? zkSyncLatestBatchQuery.data : null) ||
(hasBatches && rollupFeature.type === 'arbitrum' ? arbitrumLatestBatchQuery.data : null) || 0;
const items: Array<StatsWidgetProps> = [
hasBatches && {
icon: 'txn_batches_slim' as const, icon: 'txn_batches_slim' as const,
label: 'Latest batch', label: 'Latest batch',
value: latestBatch.toLocaleString(), value: latestBatchQuery.data.toLocaleString(),
href: { pathname: '/batches' as const }, href: { pathname: '/batches' as const },
isLoading, isLoading,
}, },
!hasBatches && { {
id: 'total_blocks' as const,
icon: 'block_slim' as const, icon: 'block_slim' as const,
label: 'Total blocks', label: 'Total blocks',
value: Number(data.total_blocks).toLocaleString(), value: Number(data.total_blocks).toLocaleString(),
href: { pathname: '/blocks' as const }, href: { pathname: '/blocks' as const },
isLoading, isLoading,
}, },
hasAvgBlockTime && { {
id: 'average_block_time' as const,
icon: 'clock-light' as const, icon: 'clock-light' as const,
label: 'Average block time', label: 'Average block time',
value: `${ (data.average_block_time / 1000).toFixed(1) }s`, value: `${ (data.average_block_time / 1000).toFixed(1) }s`,
isLoading, isLoading,
}, },
{ {
id: 'total_txs' as const,
icon: 'transactions_slim' as const, icon: 'transactions_slim' as const,
label: 'Total transactions', label: 'Total transactions',
value: Number(data.total_transactions).toLocaleString(), value: Number(data.total_transactions).toLocaleString(),
href: { pathname: '/txs' as const }, href: { pathname: '/txs' as const },
isLoading, isLoading,
}, },
rollupFeature.isEnabled && data.last_output_root_size && { data.last_output_root_size && {
id: 'latest_l1_state_batch' as const,
icon: 'txn_batches_slim' as const, icon: 'txn_batches_slim' as const,
label: 'Latest L1 state batch', label: 'Latest L1 state batch',
value: data.last_output_root_size, value: data.last_output_root_size,
...@@ -122,12 +139,14 @@ const Stats = () => { ...@@ -122,12 +139,14 @@ const Stats = () => {
isLoading, isLoading,
}, },
{ {
id: 'wallet_addresses' as const,
icon: 'wallet' as const, icon: 'wallet' as const,
label: 'Wallet addresses', label: 'Wallet addresses',
value: Number(data.total_addresses).toLocaleString(), value: Number(data.total_addresses).toLocaleString(),
isLoading, isLoading,
}, },
hasGasTracker && data.gas_prices && { hasGasTracker && data.gas_prices && {
id: 'gas_tracker' as const,
icon: 'gas' as const, icon: 'gas' as const,
label: 'Gas tracker', label: 'Gas tracker',
value: data.gas_prices.average ? <GasPrice data={ data.gas_prices.average }/> : 'N/A', value: data.gas_prices.average ? <GasPrice data={ data.gas_prices.average }/> : 'N/A',
...@@ -135,33 +154,39 @@ const Stats = () => { ...@@ -135,33 +154,39 @@ const Stats = () => {
isLoading, isLoading,
}, },
data.rootstock_locked_btc && { data.rootstock_locked_btc && {
id: 'btc_locked' as const,
icon: 'coins/bitcoin' as const, icon: 'coins/bitcoin' as const,
label: 'BTC Locked in 2WP', label: 'BTC Locked in 2WP',
value: `${ BigNumber(data.rootstock_locked_btc).div(WEI).dp(0).toFormat() } RBTC`, value: `${ BigNumber(data.rootstock_locked_btc).div(WEI).dp(0).toFormat() } RBTC`,
isLoading, isLoading,
}, },
data.celo && { data.celo && {
id: 'current_epoch' as const,
icon: 'hourglass' as const, icon: 'hourglass' as const,
label: 'Current epoch', label: 'Current epoch',
value: `#${ data.celo.epoch_number }`, value: `#${ data.celo.epoch_number }`,
isLoading, isLoading,
}, },
].filter(Boolean); ]
.filter(Boolean)
return ( .filter(({ id }) => config.UI.homepage.stats.includes(id))
<> .sort((a, b) => {
{ items.map((item, index) => ( const indexA = config.UI.homepage.stats.indexOf(a.id);
<StatsWidget const indexB = config.UI.homepage.stats.indexOf(b.id);
key={ item.icon } if (indexA > indexB) {
{ ...item } return 1;
isLoading={ isLoading } }
_last={ items.length % 2 === 1 && index === items.length - 1 ? { gridColumn: 'span 2' } : undefined }/> if (indexA < indexB) {
), return -1;
) } }
</> return 0;
); });
})(); })();
if (items.length === 0) {
return null;
}
return ( return (
<Grid <Grid
gridTemplateColumns="1fr 1fr" gridTemplateColumns="1fr 1fr"
...@@ -169,7 +194,14 @@ const Stats = () => { ...@@ -169,7 +194,14 @@ const Stats = () => {
flexBasis="50%" flexBasis="50%"
flexGrow={ 1 } flexGrow={ 1 }
> >
{ content } { items.map((item, index) => (
<StatsWidget
key={ item.id }
{ ...item }
isLoading={ isLoading }
_last={ items.length % 2 === 1 && index === items.length - 1 ? { gridColumn: 'span 2' } : undefined }/>
),
) }
</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