Commit 25305586 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into fix-ci-cd

parents 72f0706a c256c241
export default function hexToAddress(hex: string) {
const shortenHex = hex.slice(0, 66);
return shortenHex.slice(0, 2) + shortenHex.slice(26);
}
export default function hexToBytes(hex: string) {
const bytes = [];
for (let c = 0; c < hex.length; c += 2) {
bytes.push(parseInt(hex.substring(c, c + 2), 16));
}
return bytes;
}
import hexToBytes from 'lib/hexToBytes';
export default function hexToUtf8(hex: string) {
const utf8decoder = new TextDecoder();
const bytes = new Uint8Array(hexToBytes(hex));
return utf8decoder.decode(bytes);
}
......@@ -29,7 +29,7 @@ export default function useNavItems() {
return React.useMemo(() => {
const mainNavItems = [
{ text: 'Blocks', url: link('blocks'), icon: blocksIcon, isActive: currentRoute.startsWith('block') },
{ text: 'Transactions', url: link('txs_validated'), icon: transactionsIcon, isActive: currentRoute.startsWith('tx') },
{ text: 'Transactions', url: link('txs'), icon: transactionsIcon, isActive: currentRoute.startsWith('tx') },
{ text: 'Tokens', url: link('tokens'), icon: tokensIcon, isActive: currentRoute === 'tokens' },
isMarketplaceFilled ?
{ text: 'Apps', url: link('apps'), icon: appsIcon, isActive: currentRoute === 'apps' } : null,
......@@ -41,7 +41,7 @@ export default function useNavItems() {
const accountNavItems = [
{ text: 'Watchlist', url: link('watchlist'), icon: watchlistIcon, isActive: currentRoute === 'watchlist' },
{ text: 'Private tags', url: link('private_tags_address'), icon: privateTagIcon, isActive: currentRoute.startsWith('private_tags') },
{ text: 'Private tags', url: link('private_tags'), icon: privateTagIcon, isActive: currentRoute.startsWith('private_tags') },
{ text: 'Public tags', url: link('public_tags'), icon: publicTagIcon, isActive: currentRoute === 'public_tags' },
{ text: 'API keys', url: link('api_keys'), icon: apiKeysIcon, isActive: currentRoute === 'api_keys' },
{ text: 'Custom ABI', url: link('custom_abi'), icon: abiIcon, isActive: currentRoute === 'custom_abi' },
......
export const ACCOUNT_ROUTES: Array<RouteName> = [ 'watchlist', 'private_tags_address', 'private_tags_tx', 'public_tags', 'api_keys', 'custom_abi' ];
export const ACCOUNT_ROUTES: Array<RouteName> = [ 'watchlist', 'private_tags', 'public_tags', 'api_keys', 'custom_abi' ];
import type { RouteName } from 'lib/link/routes';
export default function isAccountRoute(route: RouteName) {
......
......@@ -20,12 +20,9 @@ export const ROUTES = {
watchlist: {
pattern: `${ BASE_PATH }/account/watchlist`,
},
private_tags_address: {
private_tags: {
pattern: `${ BASE_PATH }/account/tag_address`,
},
private_tags_tx: {
pattern: `${ BASE_PATH }/account/tag_transaction`,
},
public_tags: {
pattern: `${ BASE_PATH }/account/public_tags_request`,
},
......@@ -40,49 +37,22 @@ export const ROUTES = {
},
// TRANSACTIONS
txs_validated: {
txs: {
pattern: `${ BASE_PATH }/txs`,
crossNetworkNavigation: true,
},
txs_pending: {
pattern: `${ BASE_PATH }/pending-transactions`,
crossNetworkNavigation: true,
},
tx_index: {
tx: {
pattern: `${ BASE_PATH }/tx/[id]`,
},
tx_internal: {
pattern: `${ BASE_PATH }/tx/[id]/internal-transactions`,
},
tx_logs: {
pattern: `${ BASE_PATH }/tx/[id]/logs`,
},
tx_raw_trace: {
pattern: `${ BASE_PATH }/tx/[id]/raw-trace`,
},
tx_state: {
pattern: `${ BASE_PATH }/tx/[id]/state`,
},
// BLOCKS
blocks: {
pattern: `${ BASE_PATH }/blocks`,
crossNetworkNavigation: true,
},
blocks_uncles: {
pattern: `${ BASE_PATH }/uncles`,
crossNetworkNavigation: true,
},
blocks_reorgs: {
pattern: `${ BASE_PATH }/reorgs`,
crossNetworkNavigation: true,
},
block_index: {
block: {
pattern: `${ BASE_PATH }/block/[id]`,
},
block_txs: {
pattern: `${ BASE_PATH }/block/[id]/transactions`,
},
// TOKENS
tokens: {
......@@ -99,6 +69,10 @@ export const ROUTES = {
pattern: `${ BASE_PATH }/address/[id]`,
crossNetworkNavigation: true,
},
address_contract_verification: {
pattern: `${ BASE_PATH }/address/[id]/contract_verifications/new`,
crossNetworkNavigation: true,
},
// APPS
apps: {
......
......@@ -5,16 +5,14 @@ import React from 'react';
import type { PageParams } from './types';
import Block from 'ui/pages/Block';
import type { Props as BlockProps } from 'ui/pages/Block';
import getSeo from './getSeo';
type Props = {
pageParams: PageParams;
tab: BlockProps['tab'];
}
const BlockNextPage: NextPage<Props> = ({ pageParams, tab }: Props) => {
const BlockNextPage: NextPage<Props> = ({ pageParams }: Props) => {
const { title, description } = getSeo(pageParams);
return (
<>
......@@ -22,7 +20,7 @@ const BlockNextPage: NextPage<Props> = ({ pageParams, tab }: Props) => {
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Block tab={ tab }/>
<Block/>
</>
);
};
......
......@@ -2,26 +2,18 @@ import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import type { PageParams } from './types';
import Blocks from 'ui/pages/Blocks';
import type { Props as BlocksProps } from 'ui/pages/Blocks';
import getSeo from './getSeo';
type Props = {
pageParams: PageParams;
tab: BlocksProps['tab'];
}
const BlocksNextPage: NextPage<Props> = ({ tab }: Props) => {
const BlocksNextPage: NextPage = () => {
const { title } = getSeo();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<Blocks tab={ tab }/>
<Blocks/>
</>
);
};
......
......@@ -4,17 +4,15 @@ import React from 'react';
import type { PageParams } from './types';
import type { Props as TransactionProps } from 'ui/pages/Transaction';
import Transaction from 'ui/pages/Transaction';
import getSeo from './getSeo';
type Props = {
pageParams: PageParams;
tab: TransactionProps['tab'];
}
const TransactionNextPage: NextPage<Props> = ({ pageParams, tab }: Props) => {
const TransactionNextPage: NextPage<Props> = ({ pageParams }: Props) => {
const { title, description } = getSeo(pageParams);
return (
<>
......@@ -22,7 +20,7 @@ const TransactionNextPage: NextPage<Props> = ({ pageParams, tab }: Props) => {
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Transaction tab={ tab }/>
<Transaction/>
</>
);
};
......
......@@ -19,7 +19,7 @@ const AddressTagsPage: NextPage<Props> = () => {
return (
<>
<Head><title>{ title }</title></Head>
<PrivateTags tab="private_tags_address"/>
<PrivateTags/>
</>
);
};
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import PrivateTags from 'ui/pages/PrivateTags';
type PageParams = {
network_type: string;
network_sub_type: string;
}
type Props = {
pageParams: PageParams;
}
const TransactionTagsPage: NextPage<Props> = () => {
const title = getNetworkTitle();
return (
<>
<Head><title>{ title }</title></Head>
<PrivateTags tab="private_tags_tx"/>
</>
);
};
export default TransactionTagsPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -11,7 +11,7 @@ type Props = {
const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<BlockNextPage tab="block_txs" pageParams={ pageParams }/>
<BlockNextPage pageParams={ pageParams }/>
);
};
......
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import BlockNextPage from 'lib/next/block/BlockNextPage';
type Props = {
pageParams: PageParams;
}
const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<BlockNextPage tab="block_index" pageParams={ pageParams }/>
);
};
export default BlockPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -9,9 +9,9 @@ type Props = {
pageParams: PageParams;
}
const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
const BlockPage: NextPage<Props> = () => {
return (
<BlocksNextPage tab="blocks" pageParams={ pageParams }/>
<BlocksNextPage/>
);
};
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import Transactions from 'ui/pages/Transactions';
type PageParams = {
network_type: string;
network_sub_type: string;
}
type Props = {
pageParams: PageParams;
}
const AddressTagsPage: NextPage<Props> = () => {
const title = getNetworkTitle();
return (
<>
<Head><title>{ title }</title></Head>
<Transactions tab="txs_pending"/>
</>
);
};
export default AddressTagsPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import BlocksNextPage from 'lib/next/blocks/BlocksNextPage';
type Props = {
pageParams: PageParams;
}
const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<BlocksNextPage tab="blocks_reorgs" pageParams={ pageParams }/>
);
};
export default BlockPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -11,7 +11,7 @@ type Props = {
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<TransactionNextPage tab="tx_index" pageParams={ pageParams }/>
<TransactionNextPage pageParams={ pageParams }/>
);
};
......
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="tx_internal"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="tx_logs"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="tx_raw_trace"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="tx_state"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -19,7 +19,7 @@ const AddressTagsPage: NextPage<Props> = () => {
return (
<>
<Head><title>{ title }</title></Head>
<Transactions tab="txs_validated"/>
<Transactions/>
</>
);
};
......
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import BlocksNextPage from 'lib/next/blocks/BlocksNextPage';
type Props = {
pageParams: PageParams;
}
const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<BlocksNextPage tab="blocks_uncles" pageParams={ pageParams }/>
);
};
export default BlockPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -62,7 +62,7 @@ const BlockDetails = () => {
title="Transactions"
hint="The number of transactions in the block."
>
<Link href={ link('block_txs', { id: router.query.id }) }>
<Link href={ link('block', { id: router.query.id }, { tab: 'transactions' }) }>
{ block.transactionsNum } transactions
</Link>
</DetailsInfoItem>
......
......@@ -29,7 +29,7 @@ const BlocksListItem = ({ data, isPending }: Props) => {
{ isPending && <Spinner size="sm" color="blue.500" emptyColor={ spinnerEmptyColor }/> }
<Link
fontWeight={ 600 }
href={ link('block_index', { id: String(data.height) }) }
href={ link('block', { id: String(data.height) }) }
>
{ data.height }
</Link>
......
......@@ -28,7 +28,7 @@ const BlocksTableItem = ({ data, isPending }: Props) => {
{ isPending && <Spinner size="sm" color="blue.500" emptyColor={ spinnerEmptyColor }/> }
<Link
fontWeight={ 600 }
href={ link('block_index', { id: String(data.height) }) }
href={ link('block', { id: String(data.height) }) }
>
{ data.height }
</Link>
......
......@@ -10,15 +10,11 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
const TABS: Array<RoutedTab> = [
{ routeName: 'block_index', title: 'Details', component: <BlockDetails/> },
{ routeName: 'block_txs', title: 'Transactions', component: <BlockTxs/> },
{ id: 'index', title: 'Details', component: <BlockDetails/> },
{ id: 'txs', title: 'Transactions', component: <BlockTxs/> },
];
export interface Props {
tab: RoutedTab['routeName'];
}
const BlockPageContent = ({ tab }: Props) => {
const BlockPageContent = () => {
const router = useRouter();
return (
......@@ -26,7 +22,6 @@ const BlockPageContent = ({ tab }: Props) => {
<PageTitle text={ `Block #${ router.query.id || '' }` }/>
<RoutedTabs
tabs={ TABS }
defaultActiveTab={ tab }
/>
</Page>
);
......
......@@ -8,22 +8,17 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
const TABS: Array<RoutedTab> = [
{ routeName: 'blocks', title: 'All', component: <BlocksContent/> },
{ routeName: 'blocks_reorgs', title: 'Forked', component: <BlocksContent/> },
{ routeName: 'blocks_uncles', title: 'Uncles', component: <BlocksContent/> },
{ id: 'blocks', title: 'All', component: <BlocksContent/> },
{ id: 'reorgs', title: 'Forked', component: <BlocksContent/> },
{ id: 'uncles', title: 'Uncles', component: <BlocksContent/> },
];
export interface Props {
tab: RoutedTab['routeName'];
}
const BlocksPageContent = ({ tab }: Props) => {
const BlocksPageContent = () => {
return (
<Page>
<PageTitle text="Blocks"/>
<RoutedTabs
tabs={ TABS }
defaultActiveTab={ tab }
/>
</Page>
);
......
......@@ -9,19 +9,15 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
const TABS: Array<RoutedTab> = [
{ routeName: 'private_tags_address', title: 'Address', component: <PrivateAddressTags/> },
{ routeName: 'private_tags_tx', title: 'Transaction', component: <PrivateTransactionTags/> },
{ id: 'address', title: 'Address', component: <PrivateAddressTags/> },
{ id: 'tx', title: 'Transaction', component: <PrivateTransactionTags/> },
];
type Props = {
tab: RoutedTab['routeName'];
}
const PrivateTags = ({ tab }: Props) => {
const PrivateTags = () => {
return (
<Page>
<PageTitle text="Private tags"/>
<RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
<RoutedTabs tabs={ TABS }/>
</Page>
);
};
......
......@@ -16,25 +16,21 @@ import TxRawTrace from 'ui/tx/TxRawTrace';
// import TxState from 'ui/tx/TxState';
const TABS: Array<RoutedTab> = [
{ routeName: 'tx_index', title: 'Details', component: <TxDetails/> },
{ routeName: 'tx_internal', title: 'Internal txn', component: <TxInternals/> },
{ routeName: 'tx_logs', title: 'Logs', component: <TxLogs/> },
{ id: 'index', title: 'Details', component: <TxDetails/> },
{ id: 'internal', title: 'Internal txn', component: <TxInternals/> },
{ id: 'logs', title: 'Logs', component: <TxLogs/> },
// will be implemented later, api is not ready
// { routeName: 'tx_state', title: 'State', component: <TxState/> },
{ routeName: 'tx_raw_trace', title: 'Raw trace', component: <TxRawTrace/> },
// { id: 'state', title: 'State', component: <TxState/> },
{ id: 'raw_trace', title: 'Raw trace', component: <TxRawTrace/> },
];
export interface Props {
tab: RoutedTab['routeName'];
}
const TransactionPageContent = ({ tab }: Props) => {
const TransactionPageContent = () => {
const link = useLink();
return (
<Page>
{ /* TODO should be shown only when navigating from txs list */ }
<Link mb={ 6 } display="inline-flex" href={ link('txs_validated') }>
<Link mb={ 6 } display="inline-flex" href={ link('txs') }>
<Icon as={ eastArrowIcon } boxSize={ 6 } mr={ 2 } transform="rotate(180deg)"/>
Transactions
</Link>
......@@ -56,7 +52,6 @@ const TransactionPageContent = ({ tab }: Props) => {
</Flex>
<RoutedTabs
tabs={ TABS }
defaultActiveTab={ tab }
/>
</Page>
);
......
......@@ -12,21 +12,17 @@ import TxsPending from 'ui/txs/TxsPending';
import TxsValidated from 'ui/txs/TxsValidated';
const TABS: Array<RoutedTab> = [
{ routeName: 'txs_validated', title: 'Validated', component: <TxsValidated/> },
{ routeName: 'txs_pending', title: 'Pending', component: <TxsPending/> },
{ id: 'validated', title: 'Validated', component: <TxsValidated/> },
{ id: 'pending', title: 'Pending', component: <TxsPending/> },
];
type Props = {
tab: RoutedTab['routeName'];
}
const Transactions = ({ tab }: Props) => {
const Transactions = () => {
return (
<Page>
<Box h="100%">
<PageTitle text="Transactions"/>
<RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
<RoutedTabs tabs={ TABS }/>
</Box>
</Page>
);
......
import { Box, Flex, Select, Textarea } from '@chakra-ui/react';
import React from 'react';
import hexToUtf8 from 'lib/hexToUtf8';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
type DataType = 'Hex' | 'UTF-8'
......@@ -26,7 +27,7 @@ const RawInputData = ({ hex }: Props) => {
<CopyToClipboard text={ hex }/>
</Flex>
<Textarea
value={ selectedDataType === 'Hex' ? hex : 'UTF-8 equivalent' }
value={ selectedDataType === 'Hex' ? hex : hexToUtf8(hex) }
w="100%"
maxH="220px"
mt={ 2 }
......@@ -38,4 +39,4 @@ const RawInputData = ({ hex }: Props) => {
);
};
export default RawInputData;
export default React.memo(RawInputData);
......@@ -7,12 +7,11 @@ import {
} from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system';
import { useRouter } from 'next/router';
import React from 'react';
import React, { useEffect, useState } from 'react';
import type { RoutedTab } from './types';
import useIsMobile from 'lib/hooks/useIsMobile';
import { link } from 'lib/link/link';
import RoutedTabsMenu from './RoutedTabsMenu';
import useAdaptiveTabs from './useAdaptiveTabs';
......@@ -26,29 +25,36 @@ const hiddenItemStyles: StyleProps = {
interface Props {
tabs: Array<RoutedTab>;
defaultActiveTab: RoutedTab['routeName'];
}
const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
const defaultIndex = tabs.findIndex(({ routeName }) => routeName === defaultActiveTab);
const isMobile = useIsMobile();
const RoutedTabs = ({ tabs }: Props) => {
const router = useRouter();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
useEffect(() => {
if (router.isReady) {
let tabIndex = 0;
if (router.query.tab) {
tabIndex = tabs.findIndex(({ id }) => id === router.query.tab);
if (tabIndex < 0) {
tabIndex = 0;
}
}
setActiveTabIndex(tabIndex);
}
}, [ tabs, router ]);
const [ activeTab ] = React.useState<number>(defaultIndex);
const isMobile = useIsMobile();
const { tabsCut, tabsList, tabsRefs, listRef } = useAdaptiveTabs(tabs, isMobile);
const router = useRouter();
const handleTabChange = React.useCallback((index: number) => {
const nextTab = tabs[index];
if (nextTab.routeName) {
const newUrl = link(nextTab.routeName, router.query);
router.push(newUrl, undefined, { shallow: true });
}
router.query.tab = nextTab.id;
router.push(router);
}, [ tabs, router ]);
return (
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } index={ activeTab }>
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } index={ activeTabIndex }>
<TabList
marginBottom={{ base: 6, lg: 12 }}
flexWrap="nowrap"
......@@ -68,14 +74,14 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
}}
>
{ tabsList.map((tab, index) => {
if (!tab.routeName) {
if (!tab.id) {
return (
<RoutedTabsMenu
key="menu"
tabs={ tabs }
activeTab={ tabs[activeTab] }
activeTab={ tabs[activeTabIndex] }
tabsCut={ tabsCut }
isActive={ activeTab >= tabsCut }
isActive={ activeTabIndex >= tabsCut }
styles={ tabsCut < tabs.length ?
// initially our cut is 0 and we don't want to show the menu button too
// but we want to keep it in the tabs row so it won't collapse
......@@ -91,7 +97,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
return (
<Tab
key={ tab.routeName }
key={ tab.id }
ref={ tabsRefs[index] }
{ ...(index < tabsCut ? {} : hiddenItemStyles) }
scrollSnapAlign="start"
......@@ -102,7 +108,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
}) }
</TabList>
<TabPanels>
{ tabsList.map((tab) => <TabPanel padding={ 0 } key={ tab.routeName }>{ tab.component }</TabPanel>) }
{ tabsList.map((tab) => <TabPanel padding={ 0 } key={ tab.id }>{ tab.component }</TabPanel>) }
</TabPanels>
</Tabs>
);
......
......@@ -49,10 +49,10 @@ const RoutedTabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRe
<PopoverBody display="flex" flexDir="column">
{ tabs.slice(tabsCut).map((tab, index) => (
<Button
key={ tab.routeName }
key={ tab.id }
variant="ghost"
onClick={ handleItemClick }
isActive={ activeTab.routeName === tab.routeName }
isActive={ activeTab.id === tab.id }
justifyContent="left"
data-index={ index }
>
......
import type { RouteName } from 'lib/link/routes';
export interface RoutedTab {
// for simplicity we use routeName as an id
// if we migrate to non-Next.js router that should be revised
// id: string;
routeName: RouteName | null;
id: string;
title: string;
component: React.ReactNode;
}
export interface MenuButton {
routeName: null;
id: null;
title: string;
component: null;
}
......@@ -3,7 +3,7 @@ import type { MenuButton } from './types';
import { middot } from 'lib/html-entities';
export const menuButton: MenuButton = {
routeName: null,
id: null,
title: `${ middot }${ middot }${ middot }`,
component: null,
};
......@@ -19,11 +19,11 @@ const AddressLink = ({ alias, type, className, truncation = 'dynamic', hash, id,
const link = useLink();
let url;
if (type === 'transaction') {
url = link('tx_index', { id: id || hash });
url = link('tx', { id: id || hash });
} else if (type === 'token') {
url = link('token_index', { id: id || hash });
} else if (type === 'block') {
url = link('block_index', { id: id || hash });
url = link('block', { id: id || hash });
} else {
url = link('address_index', { id: id || hash });
}
......
import { Text, Grid, GridItem, Link, Tooltip, Button, Icon, useColorModeValue } from '@chakra-ui/react';
import { Text, Grid, GridItem, Tooltip, Button, useColorModeValue, Alert, Link } from '@chakra-ui/react';
import React from 'react';
import type { Log } from 'types/api/log';
import searchIcon from 'icons/search.svg';
// import searchIcon from 'icons/search.svg';
import { space } from 'lib/html-entities';
import useLink from 'lib/link/useLink';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -22,6 +24,7 @@ const TxLogItem = ({ address, index, topics, data, decoded }: Props) => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const dataBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const link = useLink();
return (
<Grid
......@@ -36,17 +39,26 @@ const TxLogItem = ({ address, index, topics, data, decoded }: Props) => {
pt: 0,
}}
>
{ !decoded && (
<GridItem colSpan={{ base: 1, lg: 2 }}>
<Alert status="warning" display="inline-table" whiteSpace="normal">
To see accurate decoded input data, the contract must be verified.{ space }
<Link href={ link('address_contract_verification', { id: address.hash }) }>Verify the contract here</Link>
</Alert>
</GridItem>
) }
<RowHeader>Address</RowHeader>
<GridItem display="flex" alignItems="center">
<Address>
<Address mr={{ base: 9, lg: 0 }}>
<AddressIcon hash={ address.hash }/>
<AddressLink hash={ address.hash } alias={ address.name } ml={ 2 }/>
</Address>
<Tooltip label="Find matches topic">
{ /* api doesn't have find topic feature yet */ }
{ /* <Tooltip label="Find matches topic">
<Link ml={ 2 } mr={{ base: 9, lg: 0 }} display="inline-flex">
<Icon as={ searchIcon } boxSize={ 5 }/>
</Link>
</Tooltip>
</Tooltip> */ }
<Tooltip label="Log index">
<Button variant="outline" colorScheme="gray" isActive ml="auto" size="sm" fontWeight={ 400 }>
{ index }
......@@ -63,7 +75,13 @@ const TxLogItem = ({ address, index, topics, data, decoded }: Props) => {
) }
<RowHeader>Topics</RowHeader>
<GridItem>
{ topics.filter(Boolean).map((item, index) => <TxLogTopic key={ index } hex={ item } index={ index }/>) }
{ topics.filter(Boolean).map((item, index) => (
<TxLogTopic
key={ index }
hex={ item }
index={ index }
/>
)) }
</GridItem>
<RowHeader>Data</RowHeader>
<GridItem p={ 4 } fontSize="sm" borderRadius="md" bgColor={ dataBgColor }>
......
import { Flex, Button, Select, Box } from '@chakra-ui/react';
import capitalize from 'lodash/capitalize';
import React from 'react';
import hexToAddress from 'lib/hexToAddress';
import hexToUtf8 from 'lib/hexToUtf8';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
......@@ -8,36 +14,71 @@ interface Props {
index: number;
}
type DataType = 'Hex' | 'Dec'
const OPTIONS: Array<DataType> = [ 'Hex', 'Dec' ];
type DataType = 'hex' | 'text' | 'address' | 'number';
const VALUE_CONVERTERS: Record<DataType, (hex: string) => string> = {
hex: (hex) => hex,
text: hexToUtf8,
address: hexToAddress,
number: (hex) => BigInt(hex).toString(),
};
const OPTIONS: Array<DataType> = [ 'hex', 'address', 'text', 'number' ];
const TxLogTopic = ({ hex, index }: Props) => {
const [ selectedDataType, setSelectedDataType ] = React.useState<DataType>('Hex');
const [ selectedDataType, setSelectedDataType ] = React.useState<DataType>('hex');
const handleSelectChange = React.useCallback((event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedDataType(event.target.value as DataType);
}, []);
const value = VALUE_CONVERTERS[selectedDataType.toLowerCase() as Lowercase<DataType>](hex);
const content = (() => {
switch (selectedDataType) {
case 'hex':
case 'number':
case 'text': {
return (
<>
<Box overflow="hidden" whiteSpace="nowrap">
<HashStringShortenDynamic hash={ value }/>
</Box>
<CopyToClipboard text={ value }/>
</>
);
}
case 'address': {
return (
<Address>
<AddressLink hash={ value }/>
<CopyToClipboard text={ value }/>
</Address>
);
}
}
})();
return (
<Flex alignItems="center" px={{ base: 0, lg: 3 }} _notFirst={{ mt: 3 }} overflow="hidden" maxW="100%">
<Button variant="outline" colorScheme="gray" isActive size="xs" fontWeight={ 400 } mr={ 3 } w={ 6 }>
{ index }
</Button>
<Select
size="sm"
borderRadius="base"
value={ selectedDataType }
onChange={ handleSelectChange }
focusBorderColor="none"
w="75px"
mr={ 3 }
flexShrink={ 0 }
>
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select>
<Box overflow="hidden" whiteSpace="nowrap">
<HashStringShortenDynamic hash={ hex }/>
</Box>
{ index !== 0 && (
<Select
size="sm"
borderRadius="base"
value={ selectedDataType }
onChange={ handleSelectChange }
focusBorderColor="none"
mr={ 3 }
flexShrink={ 0 }
w="auto"
>
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ capitalize(option) }</option>) }
</Select>
) }
{ content }
</Flex>
);
};
......
......@@ -77,7 +77,7 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => {
<Text fontWeight="600" as="span">{ tx.position }</Text>
</Box>
</Box>
<Link fontSize="sm" href={ link('tx_index', { id: tx.hash }) }>More details</Link>
<Link fontSize="sm" href={ link('tx', { id: tx.hash }) }>More details</Link>
</>
);
};
......
......@@ -78,7 +78,7 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Flex>
<Box mt={ 2 }>
<Text as="span">Block </Text>
<Link href={ link('block_index', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
</Box>
<Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address width="calc((100%-40px)/2)">
......
......@@ -104,7 +104,7 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</TruncatedTextTooltip>
</Td>
<Td>
<Link href={ link('block_index', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
</Td>
<Show above="xl">
<Td>
......
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