Commit a183ff01 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into tom2drum/issue-1035

parents 970ba614 9facb82d
......@@ -5,6 +5,9 @@ const RESTRICTED_MODULES = {
{ name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' },
{ name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' },
],
patterns: [
'icons/*',
],
};
module.exports = {
......
......@@ -14,6 +14,8 @@
/out/
/public/assets/
/public/envs.js
/public/icons/sprite.svg
/public/icons/README.md
/analyze
# production
......
......@@ -265,7 +265,7 @@
},
{
"type": "npm",
"script": "format-svg",
"script": "svg:format",
"problemMatcher": [],
"label": "format svg",
"detail": "format svg files with svgo",
......@@ -318,6 +318,7 @@
"main.L2",
"poa_core",
"eth_goerli",
"sepolia",
"eth",
"rootstock",
"polygon",
......
......@@ -58,6 +58,7 @@ RUN ./collect_envs.sh ./docs/ENVS.md
# Build app for production
RUN yarn build
RUN yarn svg:build-sprite
### FEATURE REPORTER
......
# Set of ENVs for Sepolia testnet network explorer
# https://eth-sepolia.blockscout.com/
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Sepolia
NEXT_PUBLIC_NETWORK_SHORT_NAME=Sepolia
NEXT_PUBLIC_NETWORK_ID=11155111
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth-sepolia.public.blastapi.io
NEXT_PUBLIC_IS_TESTNET=true
# api configuration
NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)'
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
## footer
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
##views
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'LooksRare','collection_url':'https://sepolia.looksrare.org/collections/{hash}','instance_url':'https://sepolia.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://sepolia.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}},{'title':'Tenderly','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/sepolia'}}]
# app features
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
......@@ -182,7 +182,8 @@ We have 3 pre-configured projects. You can run your test with the desired projec
| `yarn lint:eslint` | lint project files with ESLint |
| `yarn lint:eslint:fix` | lint project files with ESLint and automatically fix problems |
| `yarn lint:tsc` | compile project typescript files using TypeScript Compiler |
| `yarn format-svg` | format and optimize SVG icons in the `/icons` folder using SVGO tool |
| `yarn svg:format` | format and optimize SVG icons in the `/icons` folder using SVGO tool |
| `yarn svg:build-sprite` | build SVG icons sprite |
| **Testing** |
| `yarn test:jest` | run all Jest unit tests |
| `yarn test:jest:watch` | run all Jest unit tests in watch mode |
......
......@@ -4,28 +4,6 @@ import React from 'react';
import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation-items';
import config from 'configs/app';
import abiIcon from 'icons/ABI.svg';
import apiKeysIcon from 'icons/API.svg';
import appsIcon from 'icons/apps.svg';
import withdrawalsIcon from 'icons/arrows/north-east.svg';
import depositsIcon from 'icons/arrows/south-east.svg';
import blocksIcon from 'icons/block.svg';
import gearIcon from 'icons/gear.svg';
import globeIcon from 'icons/globe-b.svg';
import graphQLIcon from 'icons/graphQL.svg';
import outputRootsIcon from 'icons/output_roots.svg';
import privateTagIcon from 'icons/privattags.svg';
import publicTagIcon from 'icons/publictags.svg';
import apiDocsIcon from 'icons/restAPI.svg';
import rpcIcon from 'icons/RPC.svg';
import statsIcon from 'icons/stats.svg';
import tokensIcon from 'icons/token.svg';
import topAccountsIcon from 'icons/top-accounts.svg';
import transactionsIcon from 'icons/transactions.svg';
import txnBatchIcon from 'icons/txn_batches.svg';
import verifiedIcon from 'icons/verified.svg';
import verifyContractIcon from 'icons/verify-contract.svg';
import watchlistIcon from 'icons/watchlist.svg';
import { rightLineArrow } from 'lib/html-entities';
import UserAvatar from 'ui/shared/UserAvatar';
......@@ -50,35 +28,43 @@ export default function useNavItems(): ReturnType {
return React.useMemo(() => {
let blockchainNavItems: Array<NavItem> | Array<Array<NavItem>> = [];
const topAccounts = !config.UI.views.address.hiddenViews?.top_accounts ? {
const topAccounts: NavItem | null = !config.UI.views.address.hiddenViews?.top_accounts ? {
text: 'Top accounts',
nextRoute: { pathname: '/accounts' as const },
icon: topAccountsIcon,
icon: 'top-accounts',
isActive: pathname === '/accounts',
} : null;
const blocks = {
const blocks: NavItem | null = {
text: 'Blocks',
nextRoute: { pathname: '/blocks' as const },
icon: blocksIcon,
icon: 'block',
isActive: pathname === '/blocks' || pathname === '/block/[height_or_hash]',
};
const txs = {
const txs: NavItem | null = {
text: 'Transactions',
nextRoute: { pathname: '/txs' as const },
icon: transactionsIcon,
icon: 'transactions',
isActive: pathname === '/txs' || pathname === '/tx/[hash]',
};
const verifiedContracts =
// eslint-disable-next-line max-len
{ text: 'Verified contracts', nextRoute: { pathname: '/verified-contracts' as const }, icon: verifiedIcon, isActive: pathname === '/verified-contracts' };
const verifiedContracts: NavItem | null =
{
text: 'Verified contracts',
nextRoute: { pathname: '/verified-contracts' as const },
icon: 'verified',
isActive: pathname === '/verified-contracts',
};
if (config.features.zkEvmRollup.isEnabled) {
blockchainNavItems = [
[
txs,
blocks,
// eslint-disable-next-line max-len
{ text: 'Txn batches', nextRoute: { pathname: '/zkevm-l2-txn-batches' as const }, icon: txnBatchIcon, isActive: pathname === '/zkevm-l2-txn-batches' || pathname === '/zkevm-l2-txn-batch/[number]' },
{
text: 'Txn batches',
nextRoute: { pathname: '/zkevm-l2-txn-batches' as const },
icon: 'txn_batches',
isActive: pathname === '/zkevm-l2-txn-batches' || pathname === '/zkevm-l2-txn-batch/[number]',
},
],
[
topAccounts,
......@@ -90,16 +76,16 @@ export default function useNavItems(): ReturnType {
[
txs,
// eslint-disable-next-line max-len
{ text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/l2-deposits' as const }, icon: depositsIcon, isActive: pathname === '/l2-deposits' },
{ text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/l2-deposits' as const }, icon: 'arrows/south-east', isActive: pathname === '/l2-deposits' },
// eslint-disable-next-line max-len
{ text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/l2-withdrawals' as const }, icon: withdrawalsIcon, isActive: pathname === '/l2-withdrawals' },
{ text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/l2-withdrawals' as const }, icon: 'arrows/north-east', isActive: pathname === '/l2-withdrawals' },
],
[
blocks,
// eslint-disable-next-line max-len
{ text: 'Txn batches', nextRoute: { pathname: '/l2-txn-batches' as const }, icon: txnBatchIcon, isActive: pathname === '/l2-txn-batches' },
{ text: 'Txn batches', nextRoute: { pathname: '/l2-txn-batches' as const }, icon: 'txn_batches', isActive: pathname === '/l2-txn-batches' },
// eslint-disable-next-line max-len
{ text: 'Output roots', nextRoute: { pathname: '/l2-output-roots' as const }, icon: outputRootsIcon, isActive: pathname === '/l2-output-roots' },
{ text: 'Output roots', nextRoute: { pathname: '/l2-output-roots' as const }, icon: 'output_roots', isActive: pathname === '/l2-output-roots' },
],
[
topAccounts,
......@@ -115,7 +101,7 @@ export default function useNavItems(): ReturnType {
config.features.beaconChain.isEnabled && {
text: 'Withdrawals',
nextRoute: { pathname: '/withdrawals' as const },
icon: withdrawalsIcon,
icon: 'arrows/north-east',
isActive: pathname === '/withdrawals',
},
].filter(Boolean);
......@@ -125,23 +111,23 @@ export default function useNavItems(): ReturnType {
config.features.restApiDocs.isEnabled ? {
text: 'REST API',
nextRoute: { pathname: '/api-docs' as const },
icon: apiDocsIcon,
icon: 'restAPI',
isActive: pathname === '/api-docs',
} : null,
config.features.graphqlApiDocs.isEnabled ? {
text: 'GraphQL',
nextRoute: { pathname: '/graphiql' as const },
icon: graphQLIcon,
icon: 'graphQL',
isActive: pathname === '/graphiql',
} : null,
!config.UI.sidebar.hiddenLinks?.rpc_api && {
text: 'RPC API',
icon: rpcIcon,
icon: 'RPC',
url: 'https://docs.blockscout.com/for-users/api/rpc-endpoints',
},
!config.UI.sidebar.hiddenLinks?.eth_rpc_api && {
text: 'Eth RPC API',
icon: rpcIcon,
icon: 'RPC',
url: ' https://docs.blockscout.com/for-users/api/eth-rpc',
},
].filter(Boolean);
......@@ -149,42 +135,42 @@ export default function useNavItems(): ReturnType {
const mainNavItems: ReturnType['mainNavItems'] = [
{
text: 'Blockchain',
icon: globeIcon,
icon: 'globe-b',
isActive: blockchainNavItems.flat().some(item => isInternalItem(item) && item.isActive),
subItems: blockchainNavItems,
},
{
text: 'Tokens',
nextRoute: { pathname: '/tokens' as const },
icon: tokensIcon,
icon: 'token',
isActive: pathname.startsWith('/token'),
},
config.features.marketplace.isEnabled ? {
text: 'Apps',
nextRoute: { pathname: '/apps' as const },
icon: appsIcon,
icon: 'apps',
isActive: pathname.startsWith('/app'),
} : null,
config.features.stats.isEnabled ? {
text: 'Charts & stats',
nextRoute: { pathname: '/stats' as const },
icon: statsIcon,
icon: 'stats',
isActive: pathname === '/stats',
} : null,
apiNavItems.length > 0 && {
text: 'API',
icon: apiDocsIcon,
icon: 'restAPI',
isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive),
subItems: apiNavItems,
},
{
text: 'Other',
icon: gearIcon,
icon: 'gear',
subItems: [
{
text: 'Verify contract',
nextRoute: { pathname: '/contract-verification' as const },
icon: verifyContractIcon,
icon: 'verify-contract',
isActive: pathname.startsWith('/contract-verification'),
},
...config.UI.sidebar.otherLinks,
......@@ -196,35 +182,37 @@ export default function useNavItems(): ReturnType {
{
text: 'Watch list',
nextRoute: { pathname: '/account/watchlist' as const },
icon: watchlistIcon,
icon: 'watchlist',
isActive: pathname === '/account/watchlist',
},
{
text: 'Private tags',
nextRoute: { pathname: '/account/tag-address' as const },
icon: privateTagIcon,
icon: 'privattags',
isActive: pathname === '/account/tag-address',
},
{
text: 'Public tags',
nextRoute: { pathname: '/account/public-tags-request' as const },
icon: publicTagIcon, isActive: pathname === '/account/public-tags-request',
icon: 'publictags',
isActive: pathname === '/account/public-tags-request',
},
{
text: 'API keys',
nextRoute: { pathname: '/account/api-key' as const },
icon: apiKeysIcon, isActive: pathname === '/account/api-key',
icon: 'API',
isActive: pathname === '/account/api-key',
},
{
text: 'Custom ABI',
nextRoute: { pathname: '/account/custom-abi' as const },
icon: abiIcon,
icon: 'ABI',
isActive: pathname === '/account/custom-abi',
},
config.features.addressVerification.isEnabled && {
text: 'Verified addrs',
nextRoute: { pathname: '/account/verified-addresses' as const },
icon: verifiedIcon,
icon: 'verified',
isActive: pathname === '/account/verified-addresses',
},
].filter(Boolean);
......
import type { WalletType, WalletInfo } from 'types/client/wallets';
import coinbaseIcon from 'icons/wallets/coinbase.svg';
import metamaskIcon from 'icons/wallets/metamask.svg';
import tokenPocketIcon from 'icons/wallets/token-pocket.svg';
export const WALLETS_INFO: Record<Exclude<WalletType, 'none'>, WalletInfo> = {
metamask: {
name: 'MetaMask',
icon: metamaskIcon,
icon: 'wallets/metamask',
},
coinbase: {
name: 'Coinbase Wallet',
icon: coinbaseIcon,
icon: 'wallets/coinbase',
},
token_pocket: {
name: 'TokenPocket',
icon: tokenPocketIcon,
icon: 'wallets/token-pocket',
},
};
......@@ -20,7 +20,8 @@
"lint:tsc": "tsc -p ./tsconfig.json",
"lint:envs-validator:test": "cd ./deploy/tools/envs-validator && ./test.sh",
"prepare": "husky install",
"format-svg": "svgo -r ./icons",
"svg:format": "svgo -r ./icons",
"svg:build-sprite": "icons build -i ./icons -o ./public/icons --optimize",
"test:pw": "./tools/scripts/pw.sh",
"test:pw:local": "export NODE_PATH=$(pwd)/node_modules && yarn test:pw",
"test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.35.1-focal ./tools/scripts/pw.docker.sh",
......@@ -137,6 +138,7 @@
"lint-staged": ">=10",
"mockdate": "^3.0.5",
"style-loader": "^3.3.1",
"svg-icons-cli": "^0.0.5",
"svgo": "^2.8.0",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
......
......@@ -6,6 +6,7 @@ import React from 'react';
import * as serverTiming from 'nextjs/utils/serverTiming';
import theme from 'theme';
import * as svgSprite from 'ui/shared/IconSvg';
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
......@@ -48,6 +49,8 @@ class MyDocument extends Document {
<link rel="icon" sizes="16x16" type="image/png"href="/favicon/favicon-16x16.png"/>
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon-180x180.png"/>
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg"/>
<link rel="preload" as="image" href={ svgSprite.href }/>
</Head>
<body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
......
......@@ -9,5 +9,6 @@
<div id="root"></div>
<script type="module" src="/playwright/envs.js"></script>
<script type="module" src="/playwright/index.ts"></script>
<link rel="preload" as="image" href="/public/icons/sprite.svg"/>
</body>
</html>
// This file is generated by npm run build:icons
export type IconName =
| "ABI"
| "API"
| "apps"
| "arrows/down-right"
| "arrows/east-mini"
| "arrows/east"
| "arrows/north-east"
| "arrows/south-east"
| "arrows/up-down"
| "block_slim"
| "block"
| "brands/safe"
| "burger"
| "check"
| "clock-light"
| "clock"
| "coins/bitcoin"
| "collection"
| "contract_verified"
| "contract"
| "copy"
| "cross"
| "delete"
| "discussions"
| "docs"
| "donate"
| "edit"
| "email-sent"
| "email"
| "empty_search_result"
| "error-pages/404"
| "error-pages/422"
| "error-pages/429"
| "error-pages/500"
| "explorer"
| "files/csv"
| "files/image"
| "files/json"
| "files/placeholder"
| "files/sol"
| "files/yul"
| "filter"
| "finalized"
| "flame"
| "gas"
| "gear"
| "globe-b"
| "globe"
| "graphQL"
| "info"
| "key"
| "link"
| "lock"
| "minus"
| "monaco/file"
| "monaco/folder-open"
| "monaco/folder"
| "monaco/solidity"
| "monaco/vyper"
| "moon-with-star"
| "moon"
| "networks"
| "networks/icon-placeholder"
| "networks/logo-placeholder"
| "nft_shield"
| "output_roots"
| "plus"
| "privattags"
| "profile"
| "publictags_slim"
| "publictags"
| "qr_code"
| "repeat_arrow"
| "restAPI"
| "rocket"
| "RPC"
| "scope"
| "score/score-not-ok"
| "score/score-ok"
| "search"
| "social/canny"
| "social/coingecko"
| "social/coinmarketcap"
| "social/defi_llama"
| "social/discord_filled"
| "social/discord"
| "social/facebook_filled"
| "social/git"
| "social/github_filled"
| "social/linkedin_filled"
| "social/medium_filled"
| "social/opensea_filled"
| "social/reddit_filled"
| "social/slack_filled"
| "social/stats"
| "social/telega"
| "social/telegram_filled"
| "social/tweet"
| "social/twitter_filled"
| "star_filled"
| "star_outline"
| "stats"
| "status/error"
| "status/pending"
| "status/success"
| "status/warning"
| "sun"
| "testnet"
| "token-placeholder"
| "token"
| "tokens"
| "tokens/xdai"
| "top-accounts"
| "transactions_slim"
| "transactions"
| "txn_batches_slim"
| "txn_batches"
| "unfinalized"
| "uniswap"
| "verified_token"
| "verified"
| "verify-contract"
| "vertical_dots"
| "wallet"
| "wallets/coinbase"
| "wallets/metamask"
| "wallets/token-pocket"
| "watchlist";
\ No newline at end of file
......@@ -5,11 +5,11 @@ module.exports = {
params: {
overrides: {
removeViewBox: false,
removeHiddenElems: false,
},
},
},
'removeDimensions',
'prefixIds',
],
js2svg: {
indent: 2,
......
......@@ -19,6 +19,9 @@ dotenv \
-e $config_file \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets'
yarn svg:build-sprite
echo ""
# generate envs.js file and run the app
dotenv \
-v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \
......
......@@ -8,6 +8,9 @@ dotenv \
-e .env \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets'
yarn svg:build-sprite
echo ""
# generate envs.js file and run the app
dotenv \
-v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \
......
......@@ -8,6 +8,8 @@ dotenv \
-e $config_file \
-- bash -c './deploy/scripts/make_envs_script.sh ./playwright/envs.js'
yarn svg:build-sprite
dotenv \
-v NODE_OPTIONS=\"--max-old-space-size=4096\" \
-e $config_file \
......
......@@ -2,8 +2,10 @@ import type React from 'react';
import type { Route } from 'nextjs-routes';
import type { IconName } from 'ui/shared/IconSvg';
type NavIconOrComponent = {
icon?: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
icon?: IconName;
} | {
iconComponent?: React.FC<{size?: number}>;
};
......
import type { ArrayElement } from 'types/utils';
import type { IconName } from 'ui/shared/IconSvg';
export const SUPPORTED_WALLETS = [
'metamask',
'coinbase',
......@@ -10,5 +12,5 @@ export type WalletType = ArrayElement<typeof SUPPORTED_WALLETS>;
export interface WalletInfo {
name: string;
icon: React.ElementType;
icon: IconName;
}
import { chakra, Icon, Tooltip, Hide, Skeleton, Flex } from '@chakra-ui/react';
import { chakra, Tooltip, Hide, Skeleton, Flex } from '@chakra-ui/react';
import React from 'react';
import type { CsvExportParams } from 'types/client/address';
......@@ -6,9 +6,9 @@ import type { CsvExportParams } from 'types/client/address';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal';
interface Props {
......@@ -47,7 +47,7 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
href={ route({ pathname: '/csv-export', query: { ...params, address } }) }
flexShrink={ 0 }
>
<Icon as={ svgFileIcon } boxSize={{ base: '30px', lg: 6 }}/>
<IconSvg name="files/csv" boxSize={{ base: '30px', lg: 6 }}/>
<Hide ssr={ false } below="lg"><chakra.span ml={ 1 }>Download CSV</chakra.span></Hide>
</LinkInternal>
</Tooltip>
......
......@@ -5,8 +5,6 @@ import React from 'react';
import type { NFTTokenType } from 'types/api/token';
import type { PaginationParams } from 'ui/shared/pagination/types';
import listIcon from 'icons/apps.svg';
import collectionIcon from 'icons/collection.svg';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
......@@ -125,8 +123,8 @@ const AddressTokens = () => {
defaultValue={ nftDisplayType }
name="type"
options={ [
{ title: 'By collection', value: 'collection', icon: collectionIcon, onlyIcon: isMobile },
{ title: 'List', value: 'list', icon: listIcon, onlyIcon: isMobile },
{ title: 'By collection', value: 'collection', icon: 'collection', onlyIcon: isMobile },
{ title: 'List', value: 'list', icon: 'apps', onlyIcon: isMobile },
] }
/>
);
......
......@@ -4,7 +4,6 @@ import {
Text,
Grid,
Button,
Icon,
chakra,
Popover,
PopoverTrigger,
......@@ -19,10 +18,9 @@ import React from 'react';
import { SolidityscanReport } from 'types/api/contract';
import scoreNotOkIcon from 'icons/score/score-not-ok.svg';
import scoreOkIcon from 'icons/score/score-ok.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { SOLIDITYSCAN_REPORT } from 'stubs/contract';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal';
type DistributionItem = {
......@@ -125,7 +123,7 @@ const SolidityscanReport = ({ className, hash }: Props) => {
h="32px"
flexShrink={ 0 }
>
<Icon as={ score < 80 ? scoreNotOkIcon : scoreOkIcon } boxSize={ 5 } mr={ 1 }/>
<IconSvg name={ score < 80 ? 'score/score-not-ok' : 'score/score-ok' } boxSize={ 5 } mr={ 1 }/>
{ score }
</Button>
</Skeleton>
......@@ -143,7 +141,7 @@ const SolidityscanReport = ({ className, hash }: Props) => {
mr={ 3 }
>
<Center position="absolute" w="38px" h="38px" top="5px" right="5px" bg={ popoverBgColor } borderRadius="20px">
<Icon as={ score < 80 ? scoreNotOkIcon : scoreOkIcon } boxSize={ 5 } color={ scoreColor }/>
<IconSvg name={ score < 80 ? 'score/score-not-ok' : 'score/score-ok' } boxSize={ 5 } color={ scoreColor }/>
</Center>
</Box>
<Box>
......
......@@ -78,6 +78,31 @@ test('verified with changed byte code socket', async({ mount, page, createSocket
await expect(component).toHaveScreenshot();
});
test('verified via lookup in eth_bytecode_db', async({ mount, page, createSocket }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.nonVerified),
}));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
await mount(
<TestApp withSocket>
<ContractCode addressHash={ addressHash }/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:' + addressHash.toLowerCase());
await page.waitForResponse(CONTRACT_API_URL);
socketServer.sendMessage(socket, channel, 'smart_contract_was_verified', {});
const request = await page.waitForRequest(CONTRACT_API_URL);
expect(request).toBeTruthy();
});
test('verified with multiple sources', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
......
......@@ -38,7 +38,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>();
const queryClient = useQueryClient();
const refetchQueries = queryClient.refetchQueries;
const addressInfo = queryClient.getQueryData<AddressInfo>(getResourceKey('address', { pathParams: { hash: addressHash } }));
const { data, isPlaceholderData, isError } = useApiQuery('contract', {
......@@ -55,13 +54,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
}, [ ]);
const handleContractWasVerifiedMessage: SocketMessage.SmartContractWasVerified['handler'] = React.useCallback(() => {
refetchQueries({
queryClient.refetchQueries({
queryKey: getResourceKey('address', { pathParams: { hash: addressHash } }),
});
refetchQueries({
queryClient.refetchQueries({
queryKey: getResourceKey('contract', { pathParams: { hash: addressHash } }),
});
}, [ addressHash, refetchQueries ]);
}, [ addressHash, queryClient ]);
const enableQuery = React.useCallback(() => setIsQueryEnabled(true), []);
......
......@@ -4,7 +4,6 @@ import {
Button,
Flex,
Heading,
Icon,
Modal,
ModalCloseButton,
ModalContent,
......@@ -20,11 +19,10 @@ import React from 'react';
import type { SmartContractExternalLibrary } from 'types/api/contract';
import arrowIcon from 'icons/arrows/east-mini.svg';
import iconWarning from 'icons/status/warning.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
className?: string;
......@@ -66,8 +64,8 @@ const ContractExternalLibraries = ({ className, data }: Props) => {
aria-label="View external libraries"
>
<span>{ data.length } { data.length > 1 ? 'Libraries' : 'Library' } </span>
<Icon as={ iconWarning } boxSize={ 5 } color="orange.400" ml="2px"/>
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 2 }/>
<IconSvg name="status/warning" boxSize={ 5 } color="orange.400" ml="2px"/>
<IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 2 }/>
</Button>
);
......
import { Box, Button, chakra, Flex, Icon, Text } from '@chakra-ui/react';
import { Box, Button, chakra, Flex, Text } from '@chakra-ui/react';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm, FormProvider } from 'react-hook-form';
......@@ -7,8 +7,8 @@ import type { MethodFormFields, ContractMethodCallResult } from './types';
import type { SmartContractMethodInput, SmartContractMethod } from 'types/api/contract';
import config from 'configs/app';
import arrowIcon from 'icons/arrows/down-right.svg';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
import ContractMethodCallableRow from './ContractMethodCallableRow';
import { formatFieldValues, transformFieldsToArgs } from './utils';
......@@ -165,7 +165,7 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit,
</FormProvider>
{ 'outputs' in data && !isWrite && data.outputs.length > 0 && (
<Flex mt={ 3 } fontSize="sm">
<Icon as={ arrowIcon } boxSize={ 5 } mr={ 1 }/>
<IconSvg name="arrows/down-right" boxSize={ 5 } mr={ 1 }/>
<Text>{ data.outputs.map(({ type, name }) => [ name, type ].join(' ')).join(', ') }</Text>
</Flex>
) }
......
import { Flex, Icon, IconButton } from '@chakra-ui/react';
import { Flex, IconButton } from '@chakra-ui/react';
import React from 'react';
import type { Control, UseFormGetValues, UseFormSetValue } from 'react-hook-form';
import { useFieldArray } from 'react-hook-form';
......@@ -6,8 +6,7 @@ import { useFieldArray } from 'react-hook-form';
import type { MethodFormFields } from './types';
import type { SmartContractMethodArgType } from 'types/api/contract';
import minusIcon from 'icons/minus.svg';
import plusIcon from 'icons/plus.svg';
import IconSvg from 'ui/shared/IconSvg';
import ContractMethodField from './ContractMethodField';
......@@ -80,7 +79,7 @@ const ContractMethodFieldArray = ({ control, name, setValue, getValues, isDisabl
h="30px"
flexShrink={ 0 }
onClick={ handleRemoveButtonClick }
icon={ <Icon as={ minusIcon } boxSize={ 4 }/> }
icon={ <IconSvg name="minus" boxSize={ 4 }/> }
isDisabled={ isDisabled }
/>
) }
......@@ -93,7 +92,7 @@ const ContractMethodFieldArray = ({ control, name, setValue, getValues, isDisabl
h="30px"
flexShrink={ 0 }
onClick={ handleAddButtonClick }
icon={ <Icon as={ plusIcon } boxSize={ 4 }/> }
icon={ <IconSvg name="plus" boxSize={ 4 }/> }
isDisabled={ isDisabled }
/>
) }
......
......@@ -8,15 +8,13 @@ import {
Button,
List,
ListItem,
Icon,
useDisclosure,
Input,
} from '@chakra-ui/react';
import React from 'react';
import iconEastMini from 'icons/arrows/east-mini.svg';
import iconCheck from 'icons/check.svg';
import { times } from 'lib/html-entities';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
onClick: (power: number) => void;
......@@ -80,7 +78,7 @@ const ContractMethodFieldZeroes = ({ onClick, isDisabled }: Props) => {
onClick={ onToggle }
isDisabled={ isDisabled }
>
<Icon as={ iconEastMini } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } boxSize={ 6 }/>
<IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } boxSize={ 6 }/>
</Button>
</PopoverTrigger>
<Portal>
......@@ -99,7 +97,7 @@ const ContractMethodFieldZeroes = ({ onClick, isDisabled }: Props) => {
cursor="pointer"
>
<span>10*{ id }</span>
{ selectedOption === id && <Icon as={ iconCheck } boxSize={ 6 } color="blue.600"/> }
{ selectedOption === id && <IconSvg name="check" boxSize={ 6 } color="blue.600"/> }
</ListItem>
)) }
<ListItem
......
import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Icon, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react';
import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import { Element } from 'react-scroll';
......@@ -7,8 +7,8 @@ import type { SmartContractMethod } from 'types/api/contract';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import iconLink from 'icons/link.svg';
import Hint from 'ui/shared/Hint';
import IconSvg from 'ui/shared/IconSvg';
interface Props<T extends SmartContractMethod> {
data: T;
......@@ -58,7 +58,7 @@ const ContractMethodsAccordionItem = <T extends SmartContractMethod>({ data, ind
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
>
<Icon as={ iconLink } boxSize={ 5 }/>
<IconSvg name="link" boxSize={ 5 }/>
</Box>
</Tooltip>
) }
......
import { Icon, chakra, Tooltip, IconButton, useDisclosure } from '@chakra-ui/react';
import { chakra, Tooltip, IconButton, useDisclosure } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
import config from 'configs/app';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
import WatchlistAddModal from 'ui/watchlist/AddressModal/AddressModal';
import DeleteAddressModal from 'ui/watchlist/DeleteAddressModal';
......@@ -73,7 +72,7 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => {
pr="6px"
flexShrink={ 0 }
onClick={ handleClick }
icon={ <Icon as={ watchListId ? starFilledIcon : starOutlineIcon } boxSize={ 5 }/> }
icon={ <IconSvg name={ watchListId ? 'star_filled' : 'star_outline' } boxSize={ 5 }/> }
onFocusCapture={ onFocusCapture }
/>
</Tooltip>
......
import {
chakra,
Alert,
Icon,
Modal,
ModalBody,
ModalContent,
......@@ -22,10 +21,10 @@ import React from 'react';
import type { Address as AddressType } from 'types/api/address';
import qrCodeIcon from 'icons/qr_code.svg';
import getPageType from 'lib/mixpanel/getPageType';
import * as mixpanel from 'lib/mixpanel/index';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg';
const SVG_OPTIONS = {
margin: 0,
......@@ -78,7 +77,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
pl="6px"
pr="6px"
onClick={ onOpen }
icon={ <Icon as={ qrCodeIcon } boxSize={ 5 }/> }
icon={ <IconSvg name="qr_code" boxSize={ 5 }/> }
flexShrink={ 0 }
/>
</Tooltip>
......
......@@ -5,13 +5,12 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs';
import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import IconSvg from 'ui/shared/IconSvg';
import InOutTag from 'ui/shared/InOutTag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TxStatus from 'ui/shared/statusTag/TxStatus';
......@@ -76,7 +75,7 @@ const TxInternalsListItem = ({
/>
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
<IconSvg name="arrows/east" boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
}
{ toData && (
<AddressEntity
......
......@@ -5,13 +5,12 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import IconSvg from 'ui/shared/IconSvg';
import InOutTag from 'ui/shared/InOutTag';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
......@@ -88,7 +87,7 @@ const AddressIntTxsTableItem = ({
<Td px={ 0 } verticalAlign="middle">
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading } w="100%"/> :
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
<IconSvg name="arrows/east" boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
}
</Td>
<Td verticalAlign="middle">
......
import { Box, Flex, Icon, IconButton, Skeleton, Tooltip } from '@chakra-ui/react';
import { Box, Flex, IconButton, Skeleton, Tooltip } from '@chakra-ui/react';
import { useQueryClient, useIsFetching } from '@tanstack/react-query';
import _sumBy from 'lodash/sumBy';
import NextLink from 'next/link';
......@@ -7,11 +7,11 @@ import React from 'react';
import type { Address } from 'types/api/address';
import walletIcon from 'icons/wallet.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index';
import getQueryParamString from 'lib/router/getQueryParamString';
import IconSvg from 'ui/shared/IconSvg';
import useFetchTokens from '../utils/useFetchTokens';
import TokenSelectDesktop from './TokenSelectDesktop';
......@@ -69,7 +69,7 @@ const TokenSelect = ({ onClick }: Props) => {
size="sm"
pl="6px"
pr="6px"
icon={ <Icon as={ walletIcon } boxSize={ 5 }/> }
icon={ <IconSvg name="wallet" boxSize={ 5 }/> }
as="a"
onClick={ handleIconButtonClick }
/>
......
import { Box, Button, Icon, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import { Box, Button, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import type { FormattedData } from './types';
import arrowIcon from 'icons/arrows/east-mini.svg';
import tokensIcon from 'icons/tokens.svg';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
import { getTokensTotalInfo } from '../utils/tokenUtils';
......@@ -41,10 +40,10 @@ const TokenSelectButton = ({ isOpen, isLoading, onClick, data }: Props, ref: Rea
onClick={ handleClick }
aria-label="Token select"
>
<Icon as={ tokensIcon } boxSize={ 4 } mr={ 2 }/>
<IconSvg name="tokens" boxSize={ 4 } mr={ 2 }/>
<Text fontWeight={ 600 }>{ prefix }{ num }</Text>
<Text whiteSpace="pre" variant="secondary" fontWeight={ 400 }> ({ prefix }${ usd.toFormat(2) })</Text>
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 3 }/>
<IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 3 }/>
</Button>
{ isLoading && !isOpen && <Skeleton h="100%" w="100%" position="absolute" top={ 0 } left={ 0 } bgColor={ skeletonBgColor }/> }
</Box>
......
import { Icon, Text, Box, Input, InputGroup, InputLeftElement, useColorModeValue, Flex, Link } from '@chakra-ui/react';
import { Text, Box, Input, InputGroup, InputLeftElement, useColorModeValue, Flex, Link } from '@chakra-ui/react';
import _sumBy from 'lodash/sumBy';
import type { ChangeEvent } from 'react';
import React from 'react';
......@@ -6,8 +6,7 @@ import React from 'react';
import type { FormattedData } from './types';
import type { TokenType } from 'types/api/token';
import arrowIcon from 'icons/arrows/east.svg';
import searchIcon from 'icons/search.svg';
import IconSvg from 'ui/shared/IconSvg';
import type { Sort } from '../utils/tokenUtils';
import { sortTokenGroups, sortingFns } from '../utils/tokenUtils';
......@@ -32,7 +31,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, filteredData, onInputChange,
<>
<InputGroup size="xs" mb={ 5 }>
<InputLeftElement >
<Icon as={ searchIcon } boxSize={ 4 } color={ searchIconColor }/>
<IconSvg name="search" boxSize={ 4 } color={ searchIconColor }/>
</InputLeftElement>
<Input
paddingInlineStart="38px"
......@@ -72,7 +71,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, filteredData, onInputChange,
<Text mb={ 3 } color="gray.500" fontWeight={ 600 } fontSize="sm">{ type } tokens ({ numPrefix }{ tokenInfo.items.length })</Text>
{ hasSort && (
<Link data-type={ type } onClick={ onSortClick } aria-label={ `Sort ${ type } tokens` }>
<Icon as={ arrowIcon } boxSize={ 5 } transform={ arrowTransform } transitionDuration="faster"/>
<IconSvg name="arrows/east" boxSize={ 5 } transform={ arrowTransform } transitionDuration="faster"/>
</Link>
) }
</Flex>
......
......@@ -22,7 +22,13 @@ const ERC20TokensTableItem = ({
} = getCurrencyValue({ value: value, exchangeRate: token.exchange_rate, decimals: token.decimals, accuracy: 8, accuracyUsd: 2 });
return (
<Tr>
<Tr
sx={{
'&:hover [aria-label="Add token to wallet"]': {
opacity: 1,
},
}}
>
<Td verticalAlign="middle">
<TokenEntity
token={ token }
......@@ -39,7 +45,7 @@ const ERC20TokensTableItem = ({
isLoading={ isLoading }
noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading } opacity="0"/>
</Flex>
</Td>
<Td isNumeric verticalAlign="middle">
......
import { Box, Flex, Icon, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import { Box, Flex, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import walletIcon from 'icons/wallet.svg';
import IconSvg from 'ui/shared/IconSvg';
const TokenBalancesItem = ({ name, value, isLoading }: {name: string; value: string; isLoading: boolean }) => {
......@@ -9,7 +9,7 @@ const TokenBalancesItem = ({ name, value, isLoading }: {name: string; value: str
return (
<Flex p={ 5 } bgColor={ bgColor } borderRadius="16px" alignItems="center">
<Icon as={ walletIcon } boxSize="30px" mr={ 3 }/>
<IconSvg name="wallet" boxSize="30px" mr={ 3 }/>
<Box>
<Text variant="secondary" fontSize="xs">{ name }</Text>
<Skeleton isLoaded={ !isLoading } fontWeight="500">{ value }</Skeleton>
......
import { Icon, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Link } from '@chakra-ui/react';
import { Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Link } from '@chakra-ui/react';
import React from 'react';
import type { AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess } from './types';
import type { VerifiedAddress } from 'types/api/account';
import eastArrowIcon from 'icons/arrows/east.svg';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress';
......@@ -100,7 +100,7 @@ const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, o
<ModalHeader fontWeight="500" textStyle="h3" mb={ 6 }>
{ stepIndex !== 0 && (
<Link mr={ 3 } onClick={ handleGoToPrevStep }>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)" verticalAlign="middle"/>
<IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" verticalAlign="middle"/>
</Link>
) }
<span>{ step.title }</span>
......
......@@ -11,8 +11,6 @@ import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg';
import type { ResourceError } from 'lib/api/resources';
import getBlockReward from 'lib/block/getBlockReward';
import { GWEI, WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
......@@ -20,7 +18,6 @@ import dayjs from 'lib/date/dayjs';
import { space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString';
import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
......@@ -28,6 +25,7 @@ import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal';
import PrevNext from 'ui/shared/PrevNext';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
......@@ -169,7 +167,7 @@ const BlockDetails = ({ query }: Props) => {
hint="Date & time at which block was produced."
isLoading={ isPlaceholderData }
>
<Icon as={ clockIcon } boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<IconSvg name="clock" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<Skeleton isLoaded={ !isPlaceholderData } ml={ 1 }>
{ dayjs(data.timestamp).fromNow() }
</Skeleton>
......@@ -318,7 +316,7 @@ const BlockDetails = ({ query }: Props) => {
}
isLoading={ isPlaceholderData }
>
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }>
{ burntFees.dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</Skeleton>
......
......@@ -8,15 +8,14 @@ import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TextSeparator from 'ui/shared/TextSeparator';
......@@ -106,7 +105,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
<Text fontWeight={ 500 }>Burnt fees</Text>
<Flex columnGap={ 4 } mt={ 2 }>
<Flex>
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary" ml={ 2 }>
<span>{ burntFees.div(WEI).toFixed() }</span>
</Skeleton>
......
......@@ -8,14 +8,13 @@ import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization';
......@@ -116,7 +115,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && (
<Td fontSize="sm">
<Flex alignItems="center" columnGap={ 2 }>
<Icon as={ flameIcon } boxSize={ 5 } color={ burntFeesIconColor } isLoading={ isLoading }/>
<IconSvg name="flame" boxSize={ 5 } color={ burntFeesIconColor } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ burntFees.dividedBy(WEI).toFixed(8) }
</Skeleton>
......
import { chakra, Checkbox, Code, Icon } from '@chakra-ui/react';
import { chakra, Checkbox, Code } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
......@@ -7,10 +7,10 @@ import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/api/contract';
import iconSearch from 'icons/search.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import FancySelect from 'ui/shared/FancySelect/FancySelect';
import IconSvg from 'ui/shared/IconSvg';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
......@@ -56,7 +56,7 @@ const ContractVerificationFieldCompiler = ({ isVyper }: Props) => {
defaultOptions
size={ isMobile ? 'md' : 'lg' }
placeholder="Compiler (enter version or use the dropdown)"
placeholderIcon={ <Icon as={ iconSearch }/> }
placeholderIcon={ <IconSvg name="search"/> }
isDisabled={ formState.isSubmitting }
error={ error }
isRequired
......
import { Flex, FormControl, Icon, IconButton, Input, Text } from '@chakra-ui/react';
import { Flex, FormControl, IconButton, Input, Text } from '@chakra-ui/react';
import React from 'react';
import type { Control, ControllerRenderProps, FieldError } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import type { FormFields } from '../types';
import minusIcon from 'icons/minus.svg';
import plusIcon from 'icons/plus.svg';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import IconSvg from 'ui/shared/IconSvg';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
......@@ -86,7 +85,7 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
w="30px"
h="30px"
onClick={ handleRemoveButtonClick }
icon={ <Icon as={ minusIcon } w="20px" h="20px"/> }
icon={ <IconSvg name="minus" w="20px" h="20px"/> }
isDisabled={ isDisabled }
/>
) }
......@@ -97,7 +96,7 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
w="30px"
h="30px"
onClick={ handleAddButtonClick }
icon={ <Icon as={ plusIcon } w="20px" h="20px"/> }
icon={ <IconSvg name="plus" w="20px" h="20px"/> }
isDisabled={ isDisabled }
/>
) }
......
import {
Link,
Icon,
chakra,
Popover,
PopoverTrigger,
......@@ -20,9 +19,9 @@ import { Controller } from 'react-hook-form';
import type { FormFields } from '../types';
import type { SmartContractVerificationConfig, SmartContractVerificationMethod } from 'types/api/contract';
import infoIcon from 'icons/info.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import FancySelect from 'ui/shared/FancySelect/FancySelect';
import IconSvg from 'ui/shared/IconSvg';
import { METHOD_LABELS } from '../utils';
......@@ -105,7 +104,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props
<Popover trigger="hover" isLazy placement={ isMobile ? 'bottom-end' : 'right-start' } offset={ [ -8, 8 ] }>
<PopoverTrigger>
<chakra.span display="inline-block" ml={ 1 } cursor="pointer" verticalAlign="middle" h="22px">
<Icon as={ infoIcon } boxSize={ 5 } color="link" _hover={{ color: 'link_hovered' }}/>
<IconSvg name="info" boxSize={ 5 } color="link" _hover={{ color: 'link_hovered' }}/>
</chakra.span>
</PopoverTrigger>
<Portal>
......
......@@ -11,12 +11,11 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction';
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import IconSvg from 'ui/shared/IconSvg';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
......@@ -78,8 +77,8 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
</Box>
</Flex>
<Grid alignItems="center" alignSelf="flex-start" templateColumns="24px auto">
<Icon
as={ rightArrowIcon }
<IconSvg
name="arrows/east"
boxSize={ 6 }
color="gray.500"
transform="rotate(90deg)"
......
......@@ -10,12 +10,11 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction';
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import IconSvg from 'ui/shared/IconSvg';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
......@@ -76,8 +75,8 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
fontWeight="500"
mr={ 2 }
/>
<Icon
as={ rightArrowIcon }
<IconSvg
name="arrows/east"
boxSize={ 6 }
color="gray.500"
isLoading={ isLoading }
......
......@@ -5,13 +5,6 @@ import React from 'react';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import blockIcon from 'icons/block.svg';
import clockIcon from 'icons/clock-light.svg';
import bitcoinIcon from 'icons/coins/bitcoin.svg';
import gasIcon from 'icons/gas.svg';
import txIcon from 'icons/transactions.svg';
import batchesIcon from 'icons/txn_batches.svg';
import walletIcon from 'icons/wallet.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { WEI } from 'lib/consts';
import { HOMEPAGE_STATS } from 'stubs/stats';
......@@ -58,7 +51,7 @@ const Stats = () => {
<>
{ config.features.zkEvmRollup.isEnabled ? (
<StatsItem
icon={ batchesIcon }
icon="txn_batches"
title="Latest batch"
value={ (zkEvmLatestBatchQuery.data || 0).toLocaleString() }
url={ route({ pathname: '/zkevm-l2-txn-batches' }) }
......@@ -66,7 +59,7 @@ const Stats = () => {
/>
) : (
<StatsItem
icon={ blockIcon }
icon="block"
title="Total blocks"
value={ Number(data.total_blocks).toLocaleString() }
url={ route({ pathname: '/blocks' }) }
......@@ -75,21 +68,21 @@ const Stats = () => {
) }
{ hasAvgBlockTime && (
<StatsItem
icon={ clockIcon }
icon="clock-light"
title="Average block time"
value={ `${ (data.average_block_time / 1000).toFixed(1) }s` }
isLoading={ isPlaceholderData }
/>
) }
<StatsItem
icon={ txIcon }
icon="transactions"
title="Total transactions"
value={ Number(data.total_transactions).toLocaleString() }
url={ route({ pathname: '/txs' }) }
isLoading={ isPlaceholderData }
/>
<StatsItem
icon={ walletIcon }
icon="wallet"
title="Wallet addresses"
value={ Number(data.total_addresses).toLocaleString() }
_last={ isOdd ? lastItemTouchStyle : undefined }
......@@ -97,7 +90,7 @@ const Stats = () => {
/>
{ hasGasTracker && data.gas_prices && (
<StatsItem
icon={ gasIcon }
icon="gas"
title="Gas tracker"
value={ `${ Number(data.gas_prices.average).toLocaleString() } Gwei` }
_last={ isOdd ? lastItemTouchStyle : undefined }
......@@ -107,7 +100,7 @@ const Stats = () => {
) }
{ data.rootstock_locked_btc && (
<StatsItem
icon={ bitcoinIcon }
icon="coins/bitcoin"
title="BTC Locked in 2WP"
value={ `${ BigNumber(data.rootstock_locked_btc).div(WEI).dp(0).toFormat() } RBTC` }
_last={ isOdd ? lastItemTouchStyle : undefined }
......
......@@ -3,11 +3,12 @@ import { Skeleton, Flex, useColorModeValue, chakra, LightMode } from '@chakra-ui
import React from 'react';
import breakpoints from 'theme/foundations/breakpoints';
import Icon from 'ui/shared/chakra/Icon';
import Hint from 'ui/shared/Hint';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
type Props = {
icon: React.FC<React.SVGAttributes<SVGElement>>;
icon: IconName;
title: string;
value: string;
className?: string;
......@@ -57,7 +58,7 @@ const StatsItem = ({ icon, title, value, className, tooltipLabel, url, isLoading
href: url,
} : {}) }
>
<Icon as={ icon } boxSize={ 7 } isLoading={ isLoading } borderRadius="base"/>
<IconSvg name={ icon } boxSize={ 7 } isLoading={ isLoading } borderRadius="base"/>
<Flex
flexDirection="column"
alignItems="start"
......
import { Icon } from '@chakra-ui/react';
import React from 'react';
import type { TChainIndicator } from '../types';
import config from 'configs/app';
import globeIcon from 'icons/globe.svg';
import lockIcon from 'icons/lock.svg';
import txIcon from 'icons/transactions.svg';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg';
const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = {
id: 'daily_txs',
title: 'Daily transactions',
value: (stats) => Number(stats.transactions_today).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <Icon as={ txIcon } boxSize={ 6 } bgColor="#56ACD1" borderRadius="base" color="white"/>,
icon: <IconSvg name="transactions" boxSize={ 6 } bgColor="#56ACD1" borderRadius="base" color="white"/>,
hint: `Number of transactions yesterday (0:00 - 23:59 UTC). The chart displays daily transactions for the past 30 days.`,
api: {
resourceName: 'homepage_chart_txs',
......@@ -58,7 +55,7 @@ const marketPriceIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'market_cap',
title: 'Market cap',
value: (stats) => '$' + Number(stats.market_cap).toLocaleString(undefined, { maximumFractionDigits: 0, notation: 'compact' }),
icon: <Icon as={ globeIcon } boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
icon: <IconSvg name="globe" boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len
hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.',
api: {
......@@ -81,7 +78,7 @@ const tvlIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'tvl',
title: 'Total value locked',
value: (stats) => '$' + Number(stats.tvl).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <Icon as={ lockIcon } boxSize={ 6 } bgColor="#517FDB" borderRadius="base" color="white"/>,
icon: <IconSvg name="lock" boxSize={ 6 } bgColor="#517FDB" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len
hint: 'Total value of digital assets locked or staked in a chain',
api: {
......
import { Box, Icon, IconButton, Image, Link, LinkBox, Skeleton, useColorModeValue } from '@chakra-ui/react';
import { Box, IconButton, Image, Link, LinkBox, Skeleton, useColorModeValue } from '@chakra-ui/react';
import type { MouseEvent } from 'react';
import React, { useCallback } from 'react';
import type { MarketplaceAppPreview } from 'types/client/marketplace';
import northEastIcon from 'icons/arrows/north-east.svg';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import IconSvg from 'ui/shared/IconSvg';
import MarketplaceAppCardLink from './MarketplaceAppCardLink';
......@@ -152,8 +150,8 @@ const MarketplaceAppCard = ({
>
More
<Icon
as={ northEastIcon }
<IconSvg
name="arrows/north-east"
marginLeft={ 1 }
/>
</Link>
......@@ -175,8 +173,8 @@ const MarketplaceAppCard = ({
h={ 8 }
onClick={ handleFavoriteClick }
icon={ isFavorite ?
<Icon as={ starFilledIcon } w={ 4 } h={ 4 } color="yellow.400"/> :
<Icon as={ starOutlineIcon } w={ 4 } h={ 4 } color="gray.300"/>
<IconSvg name="star_filled" w={ 4 } h={ 4 } color="yellow.400"/> :
<IconSvg name="star_outline" w={ 4 } h={ 4 } color="gray.300"/>
}
/>
) }
......
import {
Box, Flex, Heading, Icon, IconButton, Image, Link, List, Modal, ModalBody,
Box, Flex, Heading, IconButton, Image, Link, List, Modal, ModalBody,
ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Tag, Text, useColorModeValue,
} from '@chakra-ui/react';
import React, { useCallback } from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
import linkIcon from 'icons/link.svg';
import ghIcon from 'icons/social/git.svg';
import tgIcon from 'icons/social/telega.svg';
import twIcon from 'icons/social/tweet.svg';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
import MarketplaceAppModalLink from './MarketplaceAppModalLink';
......@@ -47,15 +43,15 @@ const MarketplaceAppModal = ({
const socialLinks = [
telegram ? {
icon: tgIcon,
icon: 'social/telega' as IconName,
url: telegram,
} : null,
twitter ? {
icon: twIcon,
icon: 'social/tweet' as IconName,
url: twitter,
} : null,
github ? {
icon: ghIcon,
icon: 'social/git' as IconName,
url: github,
} : null,
].filter(Boolean);
......@@ -138,8 +134,8 @@ const MarketplaceAppModal = ({
h={ 8 }
onClick={ handleFavoriteClick }
icon={ isFavorite ?
<Icon as={ starFilledIcon } w={ 4 } h={ 4 } color="yellow.400"/> :
<Icon as={ starOutlineIcon } w={ 4 } h={ 4 } color="gray.300"/> }
<IconSvg name="star_filled" w={ 4 } h={ 4 } color="yellow.400"/> :
<IconSvg name="star_outline" w={ 4 } h={ 4 } color="gray.300"/> }
/>
</Box>
</Box>
......@@ -188,8 +184,8 @@ const MarketplaceAppModal = ({
maxW="100%"
overflow="hidden"
>
<Icon
as={ linkIcon }
<IconSvg
name="link"
display="inline"
verticalAlign="baseline"
boxSize="18px"
......@@ -227,8 +223,8 @@ const MarketplaceAppModal = ({
w={ 10 }
h={ 10 }
>
<Icon
as={ icon }
<IconSvg
name={ icon }
w="20px"
h="20px"
display="block"
......
import { Box, Button, Icon, Menu, MenuButton, MenuList, Skeleton } from '@chakra-ui/react';
import { Box, Button, Menu, MenuButton, MenuList, Skeleton } from '@chakra-ui/react';
import React from 'react';
import { MarketplaceCategory } from 'types/client/marketplace';
import eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
import IconSvg from 'ui/shared/IconSvg';
import MarketplaceCategoriesMenuItem from './MarketplaceCategoriesMenuItem';
......@@ -50,7 +50,7 @@ const MarketplaceCategoriesMenu = ({ selectedCategoryId, onSelect, categories, i
alignItems="center"
>
{ selectedCategoryId }
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
<IconSvg transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} name="arrows/east-mini" w={ 5 } h={ 5 }/>
</Box>
</MenuButton>
......
import { Icon, MenuItem } from '@chakra-ui/react';
import type { FunctionComponent, SVGAttributes } from 'react';
import { MenuItem } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import { MarketplaceCategory } from 'types/client/marketplace';
import starFilledIcon from 'icons/star_filled.svg';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
type Props = {
id: string;
onClick: (category: string) => void;
}
const ICONS: Record<string, FunctionComponent<SVGAttributes<SVGElement>>> = {
[MarketplaceCategory.FAVORITES]: starFilledIcon,
const ICONS: Record<string, IconName> = {
[MarketplaceCategory.FAVORITES]: 'star_filled',
};
const MarketplaceCategoriesMenuItem = ({ id, onClick }: Props) => {
......@@ -27,7 +27,7 @@ const MarketplaceCategoriesMenuItem = ({ id, onClick }: Props) => {
display="flex"
alignItems="center"
>
{ id in ICONS && <Icon mr={ 3 } as={ ICONS[id] } w={ 4 } h={ 4 } color="blackAlpha.800"/> }
{ id in ICONS && <IconSvg mr={ 3 } name={ ICONS[id] } w={ 4 } h={ 4 } color="blackAlpha.800"/> }
{ id }
</MenuItem>
);
......
import { Box, Flex, HStack, Icon } from '@chakra-ui/react';
import { Box, Flex, HStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import config from 'configs/app';
import iconSuccess from 'icons/status/success.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs';
......@@ -30,6 +29,7 @@ import TextAd from 'ui/shared/ad/TextAd';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EntityTags from 'ui/shared/EntityTags';
import IconSvg from 'ui/shared/IconSvg';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
......@@ -127,7 +127,7 @@ const AddressPageContent = () => {
return (
<>
<span>Contract</span>
<Icon as={ iconSuccess } boxSize="14px" color="green.500" ml={ 1 }/>
<IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 }/>
</>
);
}
......
import { Box, Flex, Icon, Tooltip } from '@chakra-ui/react';
import { Box, Flex, Tooltip } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
......@@ -9,8 +9,6 @@ import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import config from 'configs/app';
import iconSuccess from 'icons/status/success.svg';
import iconVerifiedToken from 'icons/verified_token.svg';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs';
......@@ -30,6 +28,7 @@ import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags';
import IconSvg from 'ui/shared/IconSvg';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
......@@ -189,7 +188,7 @@ const TokenPageContent = () => {
return (
<>
<span>Contract</span>
<Icon as={ iconSuccess } boxSize="14px" color="green.500" ml={ 1 }/>
<IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 }/>
</>
);
}
......@@ -250,7 +249,7 @@ const TokenPageContent = () => {
{ verifiedInfoQuery.data?.tokenAddress && (
<Tooltip label={ `Information on this token has been verified by ${ config.chain.name }` }>
<Box boxSize={ 6 }>
<Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
<IconSvg name="verified_token" color="green.500" boxSize={ 6 } cursor="pointer"/>
</Box>
</Tooltip>
) }
......
import { Box, Text, Button, Heading, Icon, chakra } from '@chakra-ui/react';
import { Box, Text, Button, Heading, chakra } from '@chakra-ui/react';
import React from 'react';
import iconEmailSent from 'icons/email-sent.svg';
import useApiFetch from 'lib/api/useApiFetch';
import dayjs from 'lib/date/dayjs';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
email?: string; // TODO: obtain email from API
......@@ -77,7 +77,7 @@ const UnverifiedEmail = ({ email }: Props) => {
return (
<Box>
<Icon as={ iconEmailSent } width="180px" height="auto" mt="52px"/>
<IconSvg name="email-sent" width="180px" height="auto" mt="52px"/>
<Heading mt={ 6 } size="2xl">Verify your email address</Heading>
<Text variant="secondary" mt={ 3 }>
<span>Please confirm your email address to use the My Account feature. A confirmation email was sent to </span>
......
import type { InputProps } from '@chakra-ui/react';
import { IconButton, Icon, Flex } from '@chakra-ui/react';
import { IconButton, Flex } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import MinusIcon from 'icons/minus.svg';
import PlusIcon from 'icons/plus.svg';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import AddressInput from 'ui/shared/AddressInput';
import IconSvg from 'ui/shared/IconSvg';
import type { Inputs } from './PublicTagsForm';
......@@ -60,7 +59,7 @@ export default function PublicTagFormAction({ control, index, fieldsLength, erro
w="30px"
h="30px"
onClick={ onRemoveFieldClick(index) }
icon={ <Icon as={ MinusIcon } w="20px" h="20px"/> }
icon={ <IconSvg name="minus" w="20px" h="20px"/> }
/>
) }
{ index === fieldsLength - 1 && fieldsLength < MAX_INPUTS_NUM && (
......@@ -70,7 +69,7 @@ export default function PublicTagFormAction({ control, index, fieldsLength, erro
w="30px"
h="30px"
onClick={ onAddFieldClick }
icon={ <Icon as={ PlusIcon } w="20px" h="20px"/> }
icon={ <IconSvg name="plus" w="20px" h="20px"/> }
/>
) }
</Flex>
......
import { Flex, Grid, Icon, Image, Box, Text, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import { Flex, Grid, Image, Box, Text, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';
......@@ -6,9 +6,6 @@ import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes';
import labelIcon from 'icons/publictags_slim.svg';
import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
......@@ -18,6 +15,7 @@ import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
......@@ -68,7 +66,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
textOverflow="ellipsis"
/>
</LinkInternal>
{ data.is_verified_via_admin_panel && <Icon as={ verifiedToken } boxSize={ 4 } ml={ 1 } color="green.500"/> }
{ data.is_verified_via_admin_panel && <IconSvg name="verified_token" boxSize={ 4 } ml={ 1 } color="green.500"/> }
</Flex>
);
}
......@@ -107,7 +105,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
case 'label': {
return (
<Flex alignItems="center">
<Icon as={ labelIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/>
<IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
......@@ -214,12 +212,12 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
<Text whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>
</Text>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Skeleton>
<Text overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }>
{ data.token_type === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` }
{ data.token_type !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` }
</Text>
</Skeleton>
</Grid>
);
}
......@@ -245,7 +243,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
<Box overflow="hidden">
<HashStringShortenDynamic hash={ data.address }/>
</Box>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Flex>
);
}
......
import { Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import { Tr, Td, Text, Flex, Image, Box, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';
......@@ -6,9 +6,6 @@ import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes';
import labelIcon from 'icons/publictags_slim.svg';
import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
......@@ -18,6 +15,7 @@ import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
......@@ -68,7 +66,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}
/>
</LinkInternal>
{ data.is_verified_via_admin_panel && <Icon as={ verifiedToken } boxSize={ 4 } ml={ 1 } color="green.500"/> }
{ data.is_verified_via_admin_panel && <IconSvg name="verified_token" boxSize={ 4 } ml={ 1 } color="green.500"/> }
</Flex>
</Td>
<Td fontSize="sm" verticalAlign="middle">
......@@ -76,7 +74,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }>
<HashStringShortenDynamic hash={ data.address }/>
</Box>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Skeleton>
</Td>
<Td fontSize="sm" verticalAlign="middle" isNumeric>
......@@ -165,7 +163,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<>
<Td fontSize="sm">
<Flex alignItems="center">
<Icon as={ labelIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/>
<IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
......@@ -182,7 +180,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }>
<HashStringShortenDynamic hash={ data.address }/>
</Box>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Flex>
</Td>
<Td></Td>
......
import { Button, Menu, MenuButton, MenuList, Icon, Flex, Skeleton, chakra } from '@chakra-ui/react';
import { Button, Menu, MenuButton, MenuList, Flex, Skeleton, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import config from 'configs/app';
import iconArrow from 'icons/arrows/east-mini.svg';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import * as mixpanel from 'lib/mixpanel/index';
import getQueryParamString from 'lib/router/getQueryParamString';
import IconSvg from 'ui/shared/IconSvg';
import PrivateTagMenuItem from './items/PrivateTagMenuItem';
import PublicTagMenuItem from './items/PublicTagMenuItem';
......@@ -44,7 +44,7 @@ const AccountActionsMenu = ({ isLoading, className }: Props) => {
>
<Flex alignItems="center">
<span>More</span>
<Icon as={ iconArrow } transform="rotate(-90deg)" boxSize={ 5 } ml={ 1 }/>
<IconSvg name="arrows/east-mini" transform="rotate(-90deg)" boxSize={ 5 } ml={ 1 }/>
</Flex>
</MenuButton>
</Skeleton>
......
import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react';
import { MenuItem, chakra, useDisclosure } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -6,11 +6,11 @@ import React from 'react';
import type { Address } from 'types/api/address';
import type { Transaction } from 'types/api/transaction';
import iconPrivateTags from 'icons/privattags.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import getPageType from 'lib/mixpanel/getPageType';
import AddressModal from 'ui/privateTags/AddressModal/AddressModal';
import TransactionModal from 'ui/privateTags/TransactionModal/TransactionModal';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
className?: string;
......@@ -61,7 +61,7 @@ const PrivateTagMenuItem = ({ className, hash, onBeforeClick, type = 'address' }
return (
<>
<MenuItem className={ className } onClick={ handleClick }>
<Icon as={ iconPrivateTags } boxSize={ 6 } mr={ 2 }/>
<IconSvg name="privattags" boxSize={ 6 } mr={ 2 }/>
<span>Add private tag</span>
</MenuItem>
{ type === 'tx' ?
......
import { MenuItem, Icon, chakra } from '@chakra-ui/react';
import { MenuItem, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import iconPublicTags from 'icons/publictags.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
className?: string;
......@@ -23,7 +23,7 @@ const PublicTagMenuItem = ({ className, hash, onBeforeClick }: Props) => {
return (
<MenuItem className={ className }onClick={ handleClick }>
<Icon as={ iconPublicTags } boxSize={ 6 } mr={ 2 }/>
<IconSvg name="publictags" boxSize={ 6 } mr={ 2 }/>
<span>Add public tag</span>
</MenuItem>
);
......
import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react';
import { MenuItem, chakra, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { Route } from 'nextjs-routes';
import config from 'configs/app';
import iconEdit from 'icons/edit.svg';
import useApiQuery from 'lib/api/useApiQuery';
import useHasAccount from 'lib/hooks/useHasAccount';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
className?: string;
......@@ -61,7 +61,7 @@ const TokenInfoMenuItem = ({ className, hash, onBeforeClick }: Props) => {
router.push({ pathname: '/account/verified-addresses' });
}, [ router ]);
const icon = <Icon as={ iconEdit } boxSize={ 6 } mr={ 2 } p={ 1 }/>;
const icon = <IconSvg name="edit" boxSize={ 6 } mr={ 2 } p={ 1 }/>;
const content = (() => {
if (!verifiedAddressesQuery.data?.verifiedAddresses.find(({ contractAddress }) => contractAddress.toLowerCase() === hash.toLowerCase())) {
......
import {
Icon,
useColorModeValue,
chakra,
Button,
......@@ -7,7 +6,7 @@ import {
} from '@chakra-ui/react';
import React from 'react';
import infoIcon from 'icons/info.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
isOpen?: boolean;
......@@ -39,8 +38,8 @@ const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props,
cursor="pointer"
flexShrink={ 0 }
>
<Icon
as={ infoIcon }
<IconSvg
name="info"
boxSize={ 5 }
color="link"
_hover={{ color: 'link_hovered' }}
......
import { Box, HStack, Flex, Skeleton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import keyIcon from 'icons/key.svg';
import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
apiKey: string;
......@@ -14,7 +13,7 @@ interface Props {
const ApiKeySnippet = ({ apiKey, name, isLoading }: Props) => {
return (
<HStack spacing={ 2 } alignItems="start">
<Icon as={ keyIcon } boxSize={ 6 } color={ useColorModeValue('gray.500', 'gray.400') } isLoading={ isLoading }/>
<IconSvg name="key" boxSize={ 6 } color={ useColorModeValue('gray.500', 'gray.400') } isLoading={ isLoading }/>
<Box>
<Flex alignItems={{ base: 'flex-start', lg: 'center' }}>
<Skeleton isLoaded={ !isLoading } display="inline-block" fontWeight={ 600 } mr={ 1 }>
......
import { Icon } from '@chakra-ui/react';
import React from 'react';
import icon404 from 'icons/error-pages/404.svg';
import icon422 from 'icons/error-pages/422.svg';
import icon429 from 'icons/error-pages/429.svg';
import icon500 from 'icons/error-pages/500.svg';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
const ICONS: Record<string, React.FunctionComponent<React.SVGAttributes<SVGElement>> > = {
'404': icon404,
'422': icon422,
'429': icon429,
'500': icon500,
const ICONS: Record<string, IconName> = {
'404': 'error-pages/404',
'422': 'error-pages/422',
'429': 'error-pages/429',
'500': 'error-pages/500',
};
interface Props {
......@@ -18,7 +15,7 @@ interface Props {
}
const AppErrorIcon = ({ statusCode }: Props) => {
return <Icon as={ ICONS[String(statusCode)] || ICONS['500'] } width="200px" height="auto" color="text"/>;
return <IconSvg name={ ICONS[String(statusCode)] || ICONS['500'] } width="200px" height="100px" color="text"/>;
};
export default AppErrorIcon;
/* eslint-disable max-len */
import { Box, OrderedList, ListItem, Icon, useColorModeValue, Flex } from '@chakra-ui/react';
import { Box, OrderedList, ListItem, useColorModeValue, Flex } from '@chakra-ui/react';
import React from 'react';
import txIcon from 'icons/transactions.svg';
import IconSvg from 'ui/shared/IconSvg';
import AppErrorTitle from '../AppErrorTitle';
......@@ -18,7 +18,7 @@ const AppErrorInvalidTxHash = () => {
<>
<Box p={ 4 } borderColor={ snippet.borderColor } borderRadius="md" w="230px" borderWidth="1px">
<Flex alignItems="center" pb={ 4 } borderBottomWidth="1px" borderColor={ snippet.borderColor }>
<Icon as={ txIcon } boxSize={ 8 } color={ snippet.iconColor } bgColor={ snippet.iconBg } p={ 1 } borderRadius="md"/>
<IconSvg name="transactions" boxSize={ 8 } color={ snippet.iconColor } bgColor={ snippet.iconBg } p={ 1 } borderRadius="md"/>
<Box ml={ 2 }>
<Box w="125px" h="8px" borderRadius="full" bgColor={ snippet.iconBg }/>
<Box w="30px" h="8px" borderRadius="full" bgColor={ snippet.borderColor } mt={ 1.5 }/>
......
import { chakra, Icon, IconButton, useColorModeValue } from '@chakra-ui/react';
import { chakra, IconButton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import errorIcon from 'icons/status/error.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
onClick: (e: React.SyntheticEvent) => void;
......@@ -21,7 +21,7 @@ const ClearButton = ({ onClick, isDisabled, className }: Props) => {
aria-label="Clear input"
title="Clear input"
boxSize={ 6 }
icon={ <Icon as={ errorIcon } boxSize={ 3 } color={ iconColor } focusable={ false } _hover={{ color: iconColorHover }}/> }
icon={ <IconSvg name="status/error" boxSize={ 3 } color={ iconColor } _hover={{ color: iconColorHover }}/> }
size="sm"
onClick={ onClick }
/>
......
import { IconButton, Tooltip, useClipboard, chakra, useDisclosure, Skeleton } from '@chakra-ui/react';
import React, { useEffect, useState } from 'react';
import CopyIcon from 'icons/copy.svg';
import IconSvg from 'ui/shared/IconSvg';
export interface Props {
text: string;
......@@ -31,7 +31,7 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => {
<Tooltip label={ copied ? 'Copied' : 'Copy to clipboard' } isOpen={ isOpen || copied }>
<IconButton
aria-label="copy"
icon={ <CopyIcon/> }
icon={ <IconSvg name="copy" boxSize={ 5 }/> }
w="20px"
h="20px"
color="gray.400"
......
import { Box, Heading, Icon, Text } from '@chakra-ui/react';
import { Box, Heading, Text } from '@chakra-ui/react';
import React from 'react';
import emptyIcon from 'icons/empty_search_result.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
text: string;
......@@ -14,8 +14,8 @@ const EmptySearchResult = ({ text }: Props) => {
flexDirection="column"
alignItems="center"
>
<Icon
as={ emptyIcon }
<IconSvg
name="empty_search_result"
boxSize={ 60 }
display="block"
/>
......
......@@ -2,7 +2,7 @@ import type { TooltipProps } from '@chakra-ui/react';
import { chakra, IconButton, Tooltip, useDisclosure, Skeleton } from '@chakra-ui/react';
import React from 'react';
import InfoIcon from 'icons/info.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
label: string | React.ReactNode;
......@@ -21,7 +21,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
}, [ onToggle ]);
if (isLoading) {
return <Skeleton boxSize={ 5 } borderRadius="sm"/>;
return <Skeleton className={ className } boxSize={ 5 } borderRadius="sm"/>;
}
return (
......@@ -35,7 +35,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
<IconButton
colorScheme="none"
aria-label="hint"
icon={ <InfoIcon/> }
icon={ <IconSvg name="info" w="100%" h="100%"/> }
boxSize={ 5 }
variant="simple"
display="inline-block"
......
import type { HTMLChakraProps } from '@chakra-ui/react';
import { Skeleton, chakra } from '@chakra-ui/react';
import { type IconName } from 'public/icons/name';
import React from 'react';
export const href = '/icons/sprite.svg';
export { IconName };
interface Props extends HTMLChakraProps<'div'> {
name: IconName;
isLoading?: boolean;
}
const IconSvg = ({ name, isLoading, ...props }: Props) => {
return (
<Skeleton isLoaded={ !isLoading } display="inline-block" { ...props }>
<chakra.svg w="100%" h="100%">
<use href={ `${ href }#${ name }` }/>
</chakra.svg>
</Skeleton>
);
};
export default IconSvg;
import type { ChakraProps } from '@chakra-ui/react';
import { Link, Icon, chakra, Box, Skeleton, useColorModeValue } from '@chakra-ui/react';
import { Link, chakra, Box, Skeleton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import arrowIcon from 'icons/arrows/north-east.svg';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
href: string;
......@@ -59,7 +59,7 @@ const LinkExternal = ({ href, children, className, isLoading, variant }: Props)
return (
<Link className={ className } { ...styleProps } target="_blank" href={ href }>
{ children }
<Icon as={ arrowIcon } boxSize={ 4 } verticalAlign="middle" color="gray.400"/>
<IconSvg name="arrows/north-east" boxSize={ 4 } verticalAlign="middle" color="gray.400" flexShrink={ 0 }/>
</Link>
);
};
......
import { Button, Icon } from '@chakra-ui/react';
import { Button } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
......@@ -7,6 +7,7 @@ import * as mixpanel from 'lib/mixpanel/index';
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets';
import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet;
......@@ -55,7 +56,7 @@ const NetworkAddToWallet = () => {
return (
<Button variant="outline" size="sm" onClick={ handleClick }>
<Icon as={ WALLETS_INFO[wallet].icon } boxSize={ 5 } mr={ 2 }/>
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 5 } mr={ 2 }/>
Add { config.chain.name }
</Button>
);
......
import { Flex, Button, Icon, chakra, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import { Flex, Button, chakra, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import config from 'configs/app';
import arrowIcon from 'icons/arrows/east-mini.svg';
import explorerIcon from 'icons/explorer.svg';
import stripTrailingSlash from 'lib/stripTrailingSlash';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal';
interface Props {
......@@ -46,8 +45,8 @@ const NetworkExplorers = ({ className, type, pathParam }: Props) => {
h="32px"
flexShrink={ 0 }
>
<Icon as={ explorerIcon } boxSize={ 5 }/>
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 }/>
<IconSvg name="explorer" boxSize={ 5 }/>
<IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 }/>
</Button>
</PopoverTrigger>
<PopoverContent w="240px">
......
import { Heading, Flex, Tooltip, Icon, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import { Heading, Flex, Tooltip, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce';
import React from 'react';
import eastArrowIcon from 'icons/arrows/east.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal';
type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void };
......@@ -32,7 +32,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
return <Skeleton boxSize={ 6 } display="inline-block" borderRadius="base" mr={ 3 } my={ 2 } verticalAlign="text-bottom" isLoaded={ !props.isLoading }/>;
}
const icon = <Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)" margin="auto" color="gray.400"/>;
const icon = <IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" margin="auto" color="gray.400"/>;
if ('url' in props) {
return (
......
import { Icon } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import iconVerifiedToken from 'icons/verified_token.svg';
import * as addressMock from 'mocks/address/address';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags';
import IconSvg from 'ui/shared/IconSvg';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from '../PageTitle';
......@@ -33,7 +32,7 @@ const DefaultView = () => {
const contentAfter = (
<>
<Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
<IconSvg name="verified_token" color="green.500" boxSize={ 6 } cursor="pointer"/>
<EntityTags
tagsBefore={ [
{ label: 'example', display_name: 'Example label' },
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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