Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: autonomys/blockscout-frontend
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: subspace-frontend
Choose a base ref
...
head repository: blockscout/frontend
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 4 commits
  • 18 files changed
  • 1 contributor

Commits on Mar 11, 2025

  1. Copy the full SHA
    de57767 View commit details
  2. Copy the full SHA
    ae75978 View commit details

Commits on Mar 14, 2025

  1. Make static sprite registry (blockscout#2615)

    * make static sprite registry
    
    * [skip ci] fix file stats command
    
    * [skip ci] sort icons by name
    tom2drum authored Mar 14, 2025
    Copy the full SHA
    e529efa View commit details
  2. Copy the full SHA
    165f289 View commit details
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
/public/assets/configs
/public/icons/sprite.svg
/public/icons/sprite.*.svg
/public/icons/registry.json
/public/icons/README.md
/public/static/og_image.png
/public/sitemap.xml
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ RUN yarn --frozen-lockfile --network-timeout 100000
# ****** STAGE 2: Build *******
# *****************************
FROM node:22.11.0-alpine AS builder
RUN apk add --no-cache --upgrade libc6-compat bash
RUN apk add --no-cache --upgrade libc6-compat bash jq

# pass build args to env variables
ARG GIT_COMMIT_SHA
19 changes: 9 additions & 10 deletions configs/envs/.env.eth
Original file line number Diff line number Diff line change
@@ -10,19 +10,18 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws

# Instance ENVs
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "728471", "width": "728", "height": "90" }
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728470", "width": "320", "height": "100" }
NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=eth.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/eth-mainnet.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xd01175f1efa23f36c5579b3c13e2bbd0885017643a7efef5cbcb6b474384dfa8
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
@@ -32,7 +31,7 @@ NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://ethereum-mainnet.us.auth0.com/v2/logout
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Participated in our recent Blockscout activities? <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=eth" target="_blank">Check your eligibility</a> and claim your NFT Scout badges. More exciting things are coming soon!</p>
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Joined recent campaigns? Mint your Merit Badge <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=eth">here</a></p>
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=eth
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
@@ -45,11 +44,11 @@ NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Moralis','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/moralis.png','baseUrl':'https://moralis.com/','paths':{'token':'/chain/ethereum/token/price'}},{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_NAME=Ethereum
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.drpc.org
@@ -61,10 +60,10 @@ NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'Rarible','collection_url':'https://rarible.com/collection/{hash}/items','instance_url':'https://rarible.com/token/{hash}:{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/rarible.png'},{'name':'Blur','collection_url':'https://blur.io/eth/collection/{hash}','instance_url':'https://blur.io/eth/asset/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/blur.png'},{'name':'Nftrade','collection_url':'https://nftrade.com/assets/eth/{hash}','instance_url':'https://nftrade.com/assets/eth/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/nftrade.png'},{'name':'MagicEden','collection_url':'https://magiceden.io/collections/ethereum/{hash}','instance_url':'https://magiceden.io/item-details/ethereum/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/magiceden.png'}]
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/hiddenBlockBadge
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
58 changes: 54 additions & 4 deletions deploy/scripts/build_sprite.sh
Original file line number Diff line number Diff line change
@@ -1,20 +1,70 @@
#!/bin/bash

yarn icons build -i ./icons -o ./public/icons --optimize
icons_dir="./icons"
target_dir="./public/icons"

yarn icons build -i $icons_dir -o $target_dir --optimize

create_registry_file() {
# Create a temporary file to store the registry
local registry_file="$target_dir/registry.json"

# Start the JSON array
echo "[]" > "$registry_file"

# Detect OS and set appropriate stat command
get_file_size() {
local file="$1"
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
stat -f%z "$file"
else
# Linux and others
stat -c%s "$file"
fi
}

# Function to process each file
process_file() {
local file="$1"
local relative_path="${file#$icons_dir/}"
local file_size=$(get_file_size "$file")

# Create a temporary file with the new entry
jq --arg name "$relative_path" --arg size "$file_size" \
'. + [{"name": $name, "file_size": ($size|tonumber)}]' \
"$registry_file" > "${registry_file}.tmp"

# Move the temporary file back
mv "${registry_file}.tmp" "$registry_file"
}

# Find all SVG files and process them
find "$icons_dir" -type f -name "*.svg" | while read -r file; do
process_file "$file"
done
}

# Skip hash creation and renaming for playwright environment
if [ "$NEXT_PUBLIC_APP_ENV" != "pw" ]; then
# Generate hash from the sprite file
HASH=$(md5sum ./public/icons/sprite.svg | cut -d' ' -f1 | head -c 8)
HASH=$(md5sum $target_dir/sprite.svg | cut -d' ' -f1 | head -c 8)

# Remove old sprite files
rm -f ./public/icons/sprite.*.svg
rm -f $target_dir/sprite.*.svg

# Rename the new sprite file
mv ./public/icons/sprite.svg "./public/icons/sprite.${HASH}.svg"
mv $target_dir/sprite.svg "$target_dir/sprite.${HASH}.svg"

export NEXT_PUBLIC_ICON_SPRITE_HASH=${HASH}

# Skip registry creation in development environment
# just to make the dev build faster
# remove this condition if you want to create the registry file in development environment
if [ "$NEXT_PUBLIC_APP_ENV" != "development" ]; then
create_registry_file
fi

echo "SVG sprite created: sprite.${HASH}.svg"
else
echo "SVG sprite created: sprite.svg (hash skipped for playwright environment)"
10 changes: 8 additions & 2 deletions lib/address/getCheckedSummedAddress.ts
Original file line number Diff line number Diff line change
@@ -2,12 +2,18 @@ import { getAddress } from 'viem';

import config from 'configs/app';

const ERC1191_CHAIN_IDS = [
'30', // RSK Mainnet
'31', // RSK Testnet
];

export default function getCheckedSummedAddress(address: string): string {
try {
return getAddress(
address,
// We need to pass chainId to getAddress to make it work correctly for some chains, e.g. Rootstock
config.chain.id ? Number(config.chain.id) : undefined,
// We need to pass chainId to getAddress to make it work correctly for chains that support ERC-1191
// https://eips.ethereum.org/EIPS/eip-1191#usage--table
ERC1191_CHAIN_IDS.includes(config.chain.id ?? '') ? Number(config.chain.id) : undefined,
);
} catch (error) {
return address;
1 change: 0 additions & 1 deletion lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
@@ -72,7 +72,6 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/api/csrf': 'Regular page',
'/api/healthz': 'Regular page',
'/api/config': 'Regular page',
'/api/sprite': 'Regular page',
};

export default function getPageOgType(pathname: Route['pathname']) {
1 change: 0 additions & 1 deletion lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
@@ -75,7 +75,6 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/api/csrf': DEFAULT_TEMPLATE,
'/api/healthz': DEFAULT_TEMPLATE,
'/api/config': DEFAULT_TEMPLATE,
'/api/sprite': DEFAULT_TEMPLATE,
};

const TEMPLATE_MAP_ENHANCED: Partial<Record<Route['pathname'], string>> = {
1 change: 0 additions & 1 deletion lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
@@ -72,7 +72,6 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/api/csrf': '%network_name% node API CSRF token',
'/api/healthz': '%network_name% node API health check',
'/api/config': '%network_name% node API app config',
'/api/sprite': '%network_name% node API SVG sprite content',
};

const TEMPLATE_MAP_ENHANCED: Partial<Record<Route['pathname'], string>> = {
1 change: 0 additions & 1 deletion lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
@@ -70,7 +70,6 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/api/csrf': 'Node API: CSRF token',
'/api/healthz': 'Node API: Health check',
'/api/config': 'Node API: App config',
'/api/sprite': 'Node API: SVG sprite content',
};

export default function getPageType(pathname: Route['pathname']) {
1 change: 0 additions & 1 deletion nextjs/nextjs-routes.d.ts
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ declare module "nextjs-routes" {
| StaticRoute<"/api/metrics">
| StaticRoute<"/api/monitoring/invalid-api-schema">
| StaticRoute<"/api/proxy">
| StaticRoute<"/api/sprite">
| StaticRoute<"/api-docs">
| DynamicRoute<"/apps/[id]", { "id": string }>
| StaticRoute<"/apps">
7 changes: 0 additions & 7 deletions pages/404.tsx
Original file line number Diff line number Diff line change
@@ -4,19 +4,12 @@ import type { NextPageWithLayout } from 'nextjs/types';

import PageNextJs from 'nextjs/PageNextJs';

import { useRollbar } from 'lib/rollbar';
import AppError from 'ui/shared/AppError/AppError';
import LayoutError from 'ui/shared/layout/LayoutError';

const error = new Error('Not found', { cause: { status: 404 } });

const Page: NextPageWithLayout = () => {
const rollbar = useRollbar();

React.useEffect(() => {
rollbar?.error('Page not found');
}, [ rollbar ]);

return (
<PageNextJs pathname="/404">
<AppError error={ error }/>
49 changes: 0 additions & 49 deletions pages/api/sprite.ts

This file was deleted.

24 changes: 11 additions & 13 deletions ui/pages/Sprite.tsx
Original file line number Diff line number Diff line change
@@ -2,9 +2,6 @@ import { Flex, Box, Tooltip, useClipboard, useColorModeValue } from '@chakra-ui/
import { useQuery } from '@tanstack/react-query';
import React from 'react';

import type { StaticRoute } from 'nextjs-routes';
import { route } from 'nextjs-routes';

import useFetch from 'lib/hooks/useFetch';
import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
@@ -18,10 +15,10 @@ const formatFileSize = (fileSizeInBytes: number) => `${ (fileSizeInBytes / 1_024

interface IconInfo {
name: string;
fileSize: number;
file_size: number;
}

const Item = ({ name, fileSize, bgColor }: IconInfo & { bgColor: string }) => {
const Item = ({ name, file_size: fileSize, bgColor }: IconInfo & { bgColor: string }) => {
const { hasCopied, onCopy } = useClipboard(name, 1000);
const [ copied, setCopied ] = React.useState(false);

@@ -44,7 +41,7 @@ const Item = ({ name, fileSize, bgColor }: IconInfo & { bgColor: string }) => {
onClick={ onCopy }
cursor="pointer"
>
<IconSvg name={ name as IconName } boxSize="100px" bgColor={ bgColor } borderRadius="base"/>
<IconSvg name={ name.replace('.svg', '') as IconName } boxSize="100px" bgColor={ bgColor } borderRadius="base"/>
<Tooltip label={ copied ? 'Copied' : 'Copy to clipboard' } isOpen={ copied }>
<Box fontWeight={ 500 } mt={ 2 }>{ name }</Box>
</Tooltip>
@@ -61,8 +58,7 @@ const Sprite = () => {
const { data, isFetching, isError } = useQuery({
queryKey: [ 'sprite' ],
queryFn: () => {
const url = route({ pathname: '/node-api/sprite' as StaticRoute<'/api/sprite'>['pathname'] });
return fetch<{ icons: Array<IconInfo> }, unknown>(url);
return fetch<Array<IconInfo>, unknown>('/icons/registry.json');
},
});

@@ -71,11 +67,13 @@ const Sprite = () => {
return <ContentLoader/>;
}

if (isError || !data || !('icons' in data)) {
if (isError || !data || !Array.isArray(data)) {
return <DataFetchAlert/>;
}

const items = data.icons.filter((icon) => icon.name.includes(searchTerm));
const items = data
.filter((icon) => icon.name.includes(searchTerm))
.sort((a, b) => a.name.localeCompare(b.name));

if (items.length === 0) {
return <EmptySearchResult text="No icons found"/>;
@@ -89,12 +87,12 @@ const Sprite = () => {
})();

const total = React.useMemo(() => {
if (!data || !('icons' in data)) {
if (!data || !Array.isArray(data)) {
return;
}
return data?.icons.reduce((result, item) => {
return data.reduce((result, item) => {
result.num++;
result.fileSize += item.fileSize;
result.fileSize += item.file_size;
return result;
}, { num: 0, fileSize: 0 });
}, [ data ]);
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading