diff --git a/configs/app/ui.ts b/configs/app/ui.ts index f4ae2ff6e2..366ef2d5ee 100644 --- a/configs/app/ui.ts +++ b/configs/app/ui.ts @@ -1,12 +1,13 @@ import type { ContractCodeIde } from 'types/client/contract'; 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 { ColorThemeId } from 'types/settings'; import type { FontFamily } from 'types/ui'; import { COLOR_THEMES } from 'lib/settings/colorTheme'; +import * as features from './features'; import * as views from './ui/views'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils'; @@ -25,6 +26,22 @@ const hiddenLinks = (() => { return result; })(); +const homePageStats: Array = (() => { + const parsedValue = parseEnvJson>(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 parsedValue = parseEnvJson>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES')); return Array.isArray(parsedValue) ? parsedValue : []; @@ -58,12 +75,13 @@ const UI = Object.freeze({ }, homepage: { charts: parseEnvJson>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [], + stats: homePageStats, heroBanner: parseEnvJson(getEnvValue('NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG')), + // !!! DEPRECATED !!! plate: { background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND'), textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR'), }, - showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true, }, views, indexingAlert: { diff --git a/configs/envs/.env.celo_alfajores b/configs/envs/.env.celo_alfajores index 2cbb790629..2934345c90 100644 --- a/configs/envs/.env.celo_alfajores +++ b/configs/envs/.env.celo_alfajores @@ -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_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com +NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch'] \ No newline at end of file diff --git a/configs/envs/.env.jest b/configs/envs/.env.jest index e1f80c7e75..abe2107a80 100644 --- a/configs/envs/.env.jest +++ b/configs/envs/.env.jest @@ -24,7 +24,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ # ui config ## homepage NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= ## sidebar NEXT_PUBLIC_NETWORK_LOGO= diff --git a/configs/envs/.env.pw b/configs/envs/.env.pw index 1794de039d..dec30c09fe 100644 --- a/configs/envs/.env.pw +++ b/configs/envs/.env.pw @@ -25,7 +25,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ # ui config ## homepage NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true ## sidebar ## footer NEXT_PUBLIC_GIT_TAG=v1.0.11 diff --git a/configs/envs/.env.rootstock_testnet b/configs/envs/.env.rootstock_testnet index 2c3f426912..a4218a2fac 100644 --- a/configs/envs/.env.rootstock_testnet +++ b/configs/envs/.env.rootstock_testnet @@ -42,4 +42,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce'] NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com +NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','btc_locked'] \ No newline at end of file diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index fa1e1d72b4..7f31fb219d 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -29,8 +29,8 @@ import type { ValidatorsChainType } from '../../../types/client/validators'; import type { WalletType } from '../../../types/client/wallets'; import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; -import { CHAIN_INDICATOR_IDS } from '../../../types/homepage'; -import type { ChainIndicatorId, HeroBannerButtonState, HeroBannerConfig } from '../../../types/homepage'; +import { CHAIN_INDICATOR_IDS, HOME_STATS_WIDGET_IDS } 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 { COLOR_THEME_IDS } from '../../../types/settings'; import type { FontFamily } from '../../../types/ui'; @@ -567,6 +567,11 @@ const schema = yup .transform(replaceQuotes) .json() .of(yup.string().oneOf(CHAIN_INDICATOR_IDS)), + NEXT_PUBLIC_HOMEPAGE_STATS: yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.string().oneOf(HOME_STATS_WIDGET_IDS)), NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(), NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup @@ -586,7 +591,6 @@ const schema = yup const isUndefined = data === undefined; return isUndefined || heroBannerSchema.isValidSync(data); }), - NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME: yup.boolean(), // b. sidebar NEXT_PUBLIC_FEATURED_NETWORKS: yup diff --git a/deploy/tools/envs-validator/test/.env.alt b/deploy/tools/envs-validator/test/.env.alt index 62183782bf..6482ea4197 100644 --- a/deploy/tools/envs-validator/test/.env.alt +++ b/deploy/tools/envs-validator/test/.env.alt @@ -1,3 +1,4 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none NEXT_PUBLIC_API_SPEC_URL=none -NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none \ No newline at end of file +NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none +NEXT_PUBLIC_HOMEPAGE_STATS=[] \ No newline at end of file diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index 064bb48199..fc628aeefd 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -35,10 +35,10 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c18099 NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false 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_BACKGROUND='rgb(255, 145, 0)' 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_UNITS=['gwei'] NEXT_PUBLIC_IS_TESTNET=true diff --git a/docs/DEPRECATED_ENVS.md b/docs/DEPRECATED_ENVS.md index 0cf27ae106..e00aea9307 100644 --- a/docs/DEPRECATED_ENVS.md +++ b/docs/DEPRECATED_ENVS.md @@ -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_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_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 diff --git a/docs/ENVS.md b/docs/ENVS.md index cf63f0475e..fa32d893e6 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -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 | | --- | --- | --- | --- | --- | --- | --- | | 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_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+ | #### Hero banner configuration properties diff --git a/types/homepage.ts b/types/homepage.ts index 6bc73ee8d3..d8d06647c9 100644 --- a/types/homepage.ts +++ b/types/homepage.ts @@ -1,6 +1,19 @@ 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 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 { background?: Array; text_color?: Array; diff --git a/ui/home/Stats.pw.tsx b/ui/home/Stats.pw.tsx index eeaa89dc2c..9add012cba 100644 --- a/ui/home/Stats.pw.tsx +++ b/ui/home/Stats.pw.tsx @@ -9,7 +9,10 @@ import Stats from './Stats'; test.describe('all items', () => { 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); component = await render(); }); @@ -28,7 +31,7 @@ test('no gas info', async({ render, mockApiResponse }) => { test('4 items default view +@mobile -@default', async({ render, mockApiResponse, 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); const component = await render(); @@ -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 }) => { await mockEnvs([ - [ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ], - [ 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', 'false' ], + [ 'NEXT_PUBLIC_HOMEPAGE_STATS', '["total_txs","wallet_addresses","total_blocks"]' ], ]); await mockApiResponse('stats', statsMock.base); const component = await render(); diff --git a/ui/home/Stats.tsx b/ui/home/Stats.tsx index 29c2c3b96d..ab00e80cf1 100644 --- a/ui/home/Stats.tsx +++ b/ui/home/Stats.tsx @@ -2,6 +2,8 @@ import { Grid } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { HomeStatsWidgetId } from 'types/homepage'; + import config from 'configs/app'; import useApiQuery from 'lib/api/useApiQuery'; import { WEI } from 'lib/consts'; @@ -12,7 +14,6 @@ import IconSvg from 'ui/shared/IconSvg'; import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget'; import StatsWidget from 'ui/shared/stats/StatsWidget'; -const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime; const rollupFeature = config.features.rollup; const Stats = () => { @@ -35,37 +36,54 @@ const Stats = () => { const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', { queryOptions: { 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', { queryOptions: { 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', { queryOptions: { 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; } - const isLoading = isPlaceholderData || - (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' && zkEvmLatestBatchQuery.isPlaceholderData) || - (rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && zkSyncLatestBatchQuery.isPlaceholderData) || - (rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && arbitrumLatestBatchQuery.isPlaceholderData); + const isLoading = isPlaceholderData || latestBatchQuery?.isPlaceholderData; + + interface Item extends StatsWidgetProps { + id: HomeStatsWidgetId; + } - const content = (() => { + const items: Array = (() => { if (!data) { - return null; + return []; } + const gasInfoTooltip = hasGasTracker && data.gas_prices && data.gas_prices.average ? ( { ) : null; - const hasBatches = rollupFeature.isEnabled && (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum'); - const latestBatch = - (hasBatches && rollupFeature.type === 'zkEvm' ? zkEvmLatestBatchQuery.data : null) || - (hasBatches && rollupFeature.type === 'zkSync' ? zkSyncLatestBatchQuery.data : null) || - (hasBatches && rollupFeature.type === 'arbitrum' ? arbitrumLatestBatchQuery.data : null) || 0; - - const items: Array = [ - hasBatches && { + return [ + latestBatchQuery?.data !== undefined && { + id: 'latest_batch' as const, icon: 'txn_batches_slim' as const, label: 'Latest batch', - value: latestBatch.toLocaleString(), + value: latestBatchQuery.data.toLocaleString(), href: { pathname: '/batches' as const }, isLoading, }, - !hasBatches && { + { + id: 'total_blocks' as const, icon: 'block_slim' as const, label: 'Total blocks', value: Number(data.total_blocks).toLocaleString(), href: { pathname: '/blocks' as const }, isLoading, }, - hasAvgBlockTime && { + { + id: 'average_block_time' as const, icon: 'clock-light' as const, label: 'Average block time', value: `${ (data.average_block_time / 1000).toFixed(1) }s`, isLoading, }, { + id: 'total_txs' as const, icon: 'transactions_slim' as const, label: 'Total transactions', value: Number(data.total_transactions).toLocaleString(), href: { pathname: '/txs' as const }, 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, label: 'Latest L1 state batch', value: data.last_output_root_size, @@ -122,12 +139,14 @@ const Stats = () => { isLoading, }, { + id: 'wallet_addresses' as const, icon: 'wallet' as const, label: 'Wallet addresses', value: Number(data.total_addresses).toLocaleString(), isLoading, }, hasGasTracker && data.gas_prices && { + id: 'gas_tracker' as const, icon: 'gas' as const, label: 'Gas tracker', value: data.gas_prices.average ? : 'N/A', @@ -135,33 +154,39 @@ const Stats = () => { isLoading, }, data.rootstock_locked_btc && { + id: 'btc_locked' as const, icon: 'coins/bitcoin' as const, label: 'BTC Locked in 2WP', value: `${ BigNumber(data.rootstock_locked_btc).div(WEI).dp(0).toFormat() } RBTC`, isLoading, }, data.celo && { + id: 'current_epoch' as const, icon: 'hourglass' as const, label: 'Current epoch', value: `#${ data.celo.epoch_number }`, isLoading, }, - ].filter(Boolean); - - return ( - <> - { items.map((item, index) => ( - - ), - ) } - - ); + ] + .filter(Boolean) + .filter(({ id }) => config.UI.homepage.stats.includes(id)) + .sort((a, b) => { + const indexA = config.UI.homepage.stats.indexOf(a.id); + const indexB = config.UI.homepage.stats.indexOf(b.id); + if (indexA > indexB) { + return 1; + } + if (indexA < indexB) { + return -1; + } + return 0; + }); })(); + if (items.length === 0) { + return null; + } + return ( { flexBasis="50%" flexGrow={ 1 } > - { content } + { items.map((item, index) => ( + + ), + ) } ); diff --git a/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png b/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png index f53d6e247f..e9e8730a82 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png b/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png index c8348e42c7..d64b78d81c 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png differ