Commit c61b982d authored by tom's avatar tom

blocks routes

parent 9d722d82
......@@ -35,7 +35,7 @@ const oldUrls = [
},
{
oldPath: '/block/:height/transactions',
newPath: `${ PATHS.block }`,
newPath: `${ PATHS.block }?tab=txs`,
},
{
oldPath: '/address/:id/transactions',
......
......@@ -98,10 +98,10 @@ export const RESOURCES = {
filterFields: [ 'type' as const ],
},
block: {
path: '/api/v2/blocks/:id',
path: '/api/v2/blocks/:height',
},
block_txs: {
path: '/api/v2/blocks/:id/transactions',
path: '/api/v2/blocks/:height/transactions',
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
filterFields: [],
},
......
import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react';
import appConfig from 'configs/app/config';
......@@ -14,40 +16,76 @@ import tokensIcon from 'icons/token.svg';
import transactionsIcon from 'icons/transactions.svg';
import walletIcon from 'icons/wallet.svg';
import watchlistIcon from 'icons/watchlist.svg';
import link from 'lib/link/link';
import useCurrentRoute from 'lib/link/useCurrentRoute';
import notEmpty from 'lib/notEmpty';
export default function useNavItems() {
export interface NavItem {
text: string;
nextRoute: Route;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
isActive?: boolean;
isNewUi?: boolean;
}
interface ReturnType {
mainNavItems: Array<NavItem>;
accountNavItems: Array<NavItem>;
profileItem: NavItem;
}
export default function useNavItems(): ReturnType {
const isMarketplaceFilled = appConfig.marketplaceAppList.length > 0;
const currentRoute = useCurrentRoute()();
const router = useRouter();
const pathname = router.pathname;
return React.useMemo(() => {
const mainNavItems = [
{ text: 'Blocks', url: link('blocks'), icon: blocksIcon, isActive: currentRoute.startsWith('block'), isNewUi: true },
{ text: 'Transactions', url: link('txs'), icon: transactionsIcon, isActive: currentRoute.startsWith('tx'), isNewUi: true },
{ text: 'Tokens', url: link('tokens'), icon: tokensIcon, isActive: currentRoute.startsWith('token'), isNewUi: true },
{ text: 'Accounts', url: link('accounts'), icon: walletIcon, isActive: currentRoute === 'accounts', isNewUi: true },
{ text: 'Blocks', nextRoute: { pathname: '/blocks' as const }, icon: blocksIcon, isActive: pathname.startsWith('/block'), isNewUi: true },
{ text: 'Transactions', nextRoute: { pathname: '/txs' as const }, icon: transactionsIcon, isActive: pathname.startsWith('/tx'), isNewUi: true },
{ text: 'Tokens', nextRoute: { pathname: '/tokens' as const }, icon: tokensIcon, isActive: pathname.startsWith('/token'), isNewUi: true },
{ text: 'Accounts', nextRoute: { pathname: '/accounts' as const }, icon: walletIcon, isActive: pathname === '/accounts', isNewUi: true },
isMarketplaceFilled ?
{ text: 'Apps', url: link('apps'), icon: appsIcon, isActive: currentRoute.startsWith('app'), isNewUi: true } : null,
{ text: 'Charts & stats', url: link('stats'), icon: statsIcon, isActive: currentRoute === 'stats', isNewUi: true },
{ text: 'Apps', nextRoute: { pathname: '/apps' as const }, icon: appsIcon, isActive: pathname.startsWith('/app'), isNewUi: true } : null,
{ text: 'Charts & stats', nextRoute: { pathname: '/stats' as const }, icon: statsIcon, isActive: pathname === '/stats', isNewUi: true },
// there should be custom site sections like Stats, Faucet, More, etc but never an 'other'
// examples https://explorer-edgenet.polygon.technology/ and https://explorer.celo.org/
// at this stage custom menu items is under development, we will implement it later
// { text: 'Other', url: link('other'), icon: gearIcon, isActive: currentRoute === 'other' },
// { text: 'Other', url: link('other'), icon: gearIcon, isActive: pathname === 'other' },
].filter(notEmpty);
const accountNavItems = [
{ text: 'Watchlist', url: link('watchlist'), icon: watchlistIcon, isActive: currentRoute === 'watchlist', isNewUi: true },
{ text: 'Private tags', url: link('private_tags'), icon: privateTagIcon, isActive: currentRoute.startsWith('private_tags'), isNewUi: true },
{ text: 'Public tags', url: link('public_tags'), icon: publicTagIcon, isActive: currentRoute === 'public_tags', isNewUi: true },
{ text: 'API keys', url: link('api_keys'), icon: apiKeysIcon, isActive: currentRoute === 'api_keys', isNewUi: true },
{ text: 'Custom ABI', url: link('custom_abi'), icon: abiIcon, isActive: currentRoute === 'custom_abi', isNewUi: true },
{
text: 'Watchlist',
nextRoute: { pathname: '/account/watchlist' as const },
icon: watchlistIcon,
isActive: pathname === '/account/watchlist',
isNewUi: true,
},
{
text: 'Private tags',
nextRoute: { pathname: '/account/tag_address' as const },
icon: privateTagIcon,
isActive: pathname === '/account/tag_address',
isNewUi: true,
},
{
text: 'Public tags',
nextRoute: { pathname: '/account/public_tags_request' as const },
icon: publicTagIcon, isActive: pathname === '/account/public_tags_request', isNewUi: true,
},
{ text: 'API keys', nextRoute: { pathname: '/account/api_key' as const }, icon: apiKeysIcon, isActive: pathname === '/account/api_key', isNewUi: true },
{
text: 'Custom ABI',
nextRoute: { pathname: '/account/custom_abi' as const },
icon: abiIcon,
isActive: pathname === '/account/custom_abi',
isNewUi: true,
},
];
const profileItem = { text: 'My profile', url: link('profile'), icon: profileIcon, isActive: currentRoute === 'profile', isNewUi: true };
const profileItem = {
text: 'My profile', nextRoute: { pathname: '/auth/profile' as const }, icon: profileIcon, isActive: pathname === '/auth/profile', isNewUi: true };
return { mainNavItems, accountNavItems, profileItem };
}, [ isMarketplaceFilled, currentRoute ]);
}, [ isMarketplaceFilled, pathname ]);
}
......@@ -9,7 +9,7 @@
"txs": "/txs",
"tx": "/tx/:id",
"blocks": "/blocks",
"block": "/block/:id",
"block": "/block/:height",
"tokens": "/tokens",
"token_index": "/token/:hash",
"token_instance_item": "/token/:hash/instance/:id",
......
......@@ -46,13 +46,13 @@ export const ROUTES = {
},
// BLOCKS
blocks: {
pattern: PATHS.blocks,
crossNetworkNavigation: true,
},
block: {
pattern: PATHS.block,
},
// blocks: {
// pattern: PATHS.blocks,
// crossNetworkNavigation: true,
// },
// block: {
// pattern: PATHS.block,
// },
// TOKENS
tokens: {
......
import { useRouter } from 'next/router';
import React from 'react';
import type { RouteName } from 'lib/link/routes';
import { ROUTES } from 'lib/link/routes';
const PATH_PARAM_REGEXP = /\/:(\w+)/g;
export default function useCurrentRoute() {
const { route: nextRoute } = useRouter();
return React.useCallback((): RouteName => {
for (const routeName in ROUTES) {
const route = ROUTES[routeName as RouteName];
const formattedRoute = route.pattern.replace(PATH_PARAM_REGEXP, (_, paramName: string) => {
return `/[${ paramName }]`;
});
if (formattedRoute === nextRoute) {
return routeName as RouteName;
}
}
return 'network_index';
}, [ nextRoute ]);
}
import type { PageParams } from './types';
import type { RoutedQuery } from 'nextjs-routes';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params?: PageParams) {
export default function getSeo(params?: RoutedQuery<'/block/[height]'>) {
const networkTitle = getNetworkTitle();
return {
title: params ? `Block ${ params.id } - ${ networkTitle }` : '',
description: params ? `View the transactions, token transfers, and uncles for block number ${ params.id }` : '',
title: params ? `Block ${ params.height } - ${ networkTitle }` : '',
description: params ? `View the transactions, token transfers, and uncles for block number ${ params.height }` : '',
};
}
export type PageParams = {
id: string;
}
......@@ -4,6 +4,7 @@ export type Props = {
cookies: string;
referrer: string;
id?: string;
height?: string;
}
export const getServerSideProps: GetServerSideProps<Props> = async({ req, query }) => {
......@@ -12,6 +13,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, query
cookies: req.headers.cookie || '',
referrer: req.headers.referer || '',
id: query.id?.toString() || '',
height: query.height?.toString() || '',
},
};
};
export default function getQueryParamString(param: string | Array<string> | undefined): string {
if (Array.isArray(param)) {
return param.join(',');
}
return param || '';
}
import type { NextPage } from 'next';
import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react';
import type { PageParams } from 'lib/next/block/types';
import getSeo from 'lib/next/block/getSeo';
import Block from 'ui/pages/Block';
const BlockPage: NextPage<PageParams> = ({ id }: PageParams) => {
const { title, description } = getSeo({ id });
const BlockPage: NextPage<RoutedQuery<'/block/[height]'>> = ({ height }: RoutedQuery<'/block/[height]'>) => {
const { title, description } = getSeo({ height });
return (
<>
<Head>
......
import type { GetServerSideProps, NextPage } from 'next';
import Head from 'next/head';
import { route } from 'nextjs-routes';
import React from 'react';
import type { SearchRedirectResult } from 'types/api/search';
......@@ -43,7 +44,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, r
const redirectUrl = (() => {
switch (payload.type) {
case 'block': {
return link('block', { id: q });
return route({ pathname: '/block/[height]', query: { height: q } });
}
case 'address': {
return link('address_index', { id: payload.parameter || q });
......
......@@ -19,7 +19,7 @@ declare module "nextjs-routes" {
| DynamicRoute<"/apps/[id]", { "id": string }>
| StaticRoute<"/apps">
| StaticRoute<"/auth/profile">
| DynamicRoute<"/block/[id]", { "id": string }>
| DynamicRoute<"/block/[height]", { "height": string }>
| StaticRoute<"/blocks">
| StaticRoute<"/graph">
| StaticRoute<"/">
......
import { Box, Flex, Text, Icon, Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Address as TAddress } from 'types/api/address';
......@@ -201,7 +202,7 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
py={{ base: '2px', lg: 1 }}
>
<LinkInternal
href={ link('block', { id: String(data.block_number_balance_updated_at) }) }
href={ route({ pathname: '/block/[height]', query: { height: String(data.block_number_balance_updated_at) } }) }
display="flex"
alignItems="center"
>
......
......@@ -17,7 +17,7 @@ type Props = Block & {
};
const AddressBlocksValidatedListItem = (props: Props) => {
const blockUrl = route({ pathname: '/block/[id]', query: { id: props.height.toString() } });
const blockUrl = route({ pathname: '/block/[height]', query: { height: props.height.toString() } });
const timeAgo = useTimeAgoIncrement(props.timestamp, props.page === 1);
const totalReward = getBlockTotalReward(props);
......
import { Td, Tr, Text, Box, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Block } from 'types/api/block';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import LinkInternal from 'ui/shared/LinkInternal';
import Utilization from 'ui/shared/Utilization/Utilization';
......@@ -15,7 +15,7 @@ type Props = Block & {
};
const AddressBlocksValidatedTableItem = (props: Props) => {
const blockUrl = link('block', { id: String(props.height) });
const blockUrl = route({ pathname: '/block/[height]', query: { height: String(props.height) } });
const timeAgo = useTimeAgoIncrement(props.timestamp, props.page === 1);
const totalReward = getBlockTotalReward(props);
......
import { Text, Stat, StatHelpText, StatArrow, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
......@@ -7,7 +8,6 @@ import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import appConfig from 'configs/app/config';
import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -18,7 +18,7 @@ type Props = AddressCoinBalanceHistoryItem & {
};
const AddressCoinBalanceListItem = (props: Props) => {
const blockUrl = link('block', { id: String(props.block_number) });
const blockUrl = route({ pathname: '/block/[height]', query: { height: String(props.block_number) } });
const deltaBn = BigNumber(props.delta).div(WEI);
const isPositiveDelta = deltaBn.gte(ZERO);
const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1);
......
import { Td, Tr, Text, Stat, StatHelpText, StatArrow } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -16,7 +16,7 @@ type Props = AddressCoinBalanceHistoryItem & {
};
const AddressCoinBalanceTableItem = (props: Props) => {
const blockUrl = link('block', { id: String(props.block_number) });
const blockUrl = route({ pathname: '/block/[height]', query: { height: String(props.block_number) } });
const deltaBn = BigNumber(props.delta).div(WEI);
const isPositiveDelta = deltaBn.gte(ZERO);
const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1);
......
import { Flex, Tag, Icon, Box, HStack, Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
......@@ -7,7 +8,6 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config';
import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -50,7 +50,7 @@ const TxInternalsListItem = ({
</Flex>
<HStack spacing={ 1 }>
<Text fontSize="sm" fontWeight={ 500 }>Block</Text>
<LinkInternal href={ link('block', { id: block.toString() }) }>{ block }</LinkInternal>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>{ block }</LinkInternal>
</HStack>
<Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)">
......
import { Tr, Td, Tag, Icon, Box, Flex, Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
......@@ -7,7 +8,6 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config';
import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -58,7 +58,7 @@ const AddressIntTxsTableItem = ({
</Flex>
</Td>
<Td verticalAlign="middle">
<LinkInternal href={ link('block', { id: block.toString() }) }>{ block }</LinkInternal>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>{ block }</LinkInternal>
</Td>
<Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%">
......
......@@ -2,6 +2,7 @@ import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react
import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize';
import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react';
import { scroller, Element } from 'react-scroll';
......@@ -13,8 +14,8 @@ import getBlockReward from 'lib/block/getBlockReward';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import dayjs from 'lib/date/dayjs';
import { space } from 'lib/html-entities';
import link from 'lib/link/link';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString';
import BlockDetailsSkeleton from 'ui/block/details/BlockDetailsSkeleton';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
......@@ -30,10 +31,11 @@ import Utilization from 'ui/shared/Utilization/Utilization';
const BlockDetails = () => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
const router = useRouter();
const height = getQueryParamString(router.query.height);
const { data, isLoading, isError, error } = useApiQuery('block', {
pathParams: { id: router.query.id?.toString() },
queryOptions: { enabled: Boolean(router.query.id) },
pathParams: { height },
queryOptions: { enabled: Boolean(height) },
});
const handleCutClick = React.useCallback(() => {
......@@ -46,10 +48,10 @@ const BlockDetails = () => {
const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
const increment = direction === 'next' ? +1 : -1;
const nextId = String(Number(router.query.id) + increment);
const nextId = String(Number(height) + increment);
router.push({ pathname: '/block/[id]', query: { id: nextId } }, undefined);
}, [ router ]);
router.push({ pathname: '/block/[height]', query: { height: nextId } }, undefined);
}, [ height, router ]);
if (isLoading) {
return <BlockDetailsSkeleton/>;
......@@ -115,7 +117,7 @@ const BlockDetails = () => {
title="Transactions"
hint="The number of transactions in the block."
>
<LinkInternal href={ link('block', { id: router.query.id }, { tab: 'txs' }) }>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: router.query.height?.toString() || '', tab: 'txs' } }) }>
{ data.tx_count } transactions
</LinkInternal>
</DetailsInfoItem>
......
import { Flex, Spinner, Text, Box, Icon } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Block } from 'types/api/block';
......@@ -9,7 +10,6 @@ import appConfig from 'configs/app/config';
import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import link from 'lib/link/link';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -36,7 +36,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
{ isPending && <Spinner size="sm"/> }
<LinkInternal
fontWeight={ 600 }
href={ link('block', { id: String(data.height) }) }
href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) }
>
{ data.height }
</LinkInternal>
......@@ -54,7 +54,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text>
{ data.tx_count > 0 ? (
<LinkInternal href={ link('block', { id: String(data.height) }, { tab: 'txs' }) }>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }>
{ data.tx_count }
</LinkInternal>
) :
......
import { Tr, Td, Flex, Box, Icon, Tooltip, Spinner, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Block } from 'types/api/block';
......@@ -8,7 +9,6 @@ import type { Block } from 'types/api/block';
import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
......@@ -41,7 +41,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations">
<LinkInternal
fontWeight={ 600 }
href={ link('block', { id: String(data.height) }) }
href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) }
>
{ data.height }
</LinkInternal>
......@@ -55,7 +55,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
</Td>
<Td isNumeric fontSize="sm">
{ data.tx_count > 0 ? (
<LinkInternal href={ link('block', { id: String(data.height) }, { tab: 'txs' }) }>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }>
{ data.tx_count }
</LinkInternal>
) : data.tx_count }
......
import { Box, Heading, Flex, Text, VStack, Skeleton } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { AnimatePresence } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
......@@ -9,7 +10,6 @@ import type { Block } from 'types/api/block';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities';
import link from 'lib/link/link';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -98,7 +98,7 @@ const LatestBlocks = () => {
</AnimatePresence>
</VStack>
<Flex justifyContent="center">
<LinkInternal fontSize="sm" href={ link('blocks') }>View all blocks</LinkInternal>
<LinkInternal fontSize="sm" href={ route({ pathname: '/blocks' }) }>View all blocks</LinkInternal>
</Flex>
</>
);
......
......@@ -8,13 +8,13 @@ import {
Text,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Block } from 'types/api/block';
import blockIcon from 'icons/block.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -44,7 +44,7 @@ const LatestBlocksItem = ({ block, h }: Props) => {
<HStack spacing={ 2 }>
<Icon as={ blockIcon } boxSize="30px" color="link"/>
<LinkInternal
href={ link('block', { id: String(block.height) }) }
href={ route({ pathname: '/block/[height]', query: { height: String(block.height) } }) }
fontSize="xl"
fontWeight="500"
>
......
import { Grid } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
import appConfig from 'configs/app/config';
......@@ -45,7 +46,7 @@ const Stats = () => {
icon={ blockIcon }
title="Total blocks"
value={ Number(data.total_blocks).toLocaleString() }
url={ link('blocks') }
url={ route({ pathname: '/blocks' }) }
/>
{ hasAvgBlockTime && (
<StatsItem
......
......@@ -6,6 +6,7 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString';
import BlockDetails from 'ui/block/BlockDetails';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
......@@ -24,17 +25,19 @@ const BlockPageContent = () => {
const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext();
const height = getQueryParamString(router.query.height);
const tab = getQueryParamString(router.query.tab);
const blockTxsQuery = useQueryWithPages({
resourceName: 'block_txs',
pathParams: { id: router.query.id?.toString() },
pathParams: { height },
options: {
enabled: Boolean(router.query.id && router.query.tab === 'txs'),
enabled: Boolean(height && tab === 'txs'),
},
});
if (!router.query.id) {
return null;
if (!height) {
throw new Error('Block not found', { cause: { status: 404 } });
}
const tabs: Array<RoutedTab> = [
......@@ -42,7 +45,7 @@ const BlockPageContent = () => {
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
];
const hasPagination = !isMobile && router.query.tab === 'txs' && blockTxsQuery.isPaginationVisible;
const hasPagination = !isMobile && tab === 'txs' && blockTxsQuery.isPaginationVisible;
const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/blocks');
......@@ -50,7 +53,7 @@ const BlockPageContent = () => {
<Page>
<TextAd mb={ 6 }/>
<PageTitle
text={ `Block #${ router.query.id }` }
text={ `Block #${ height }` }
backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined }
backLinkLabel="Back to blocks list"
/>
......
import { Text, Flex, Icon, Box, chakra } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
import type { SearchResultItem } from 'types/api/search';
......@@ -56,7 +57,7 @@ const SearchResultListItem = ({ data, searchTerm }: Props) => {
return (
<Flex alignItems="center">
<Icon as={ blockIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal fontWeight={ 700 } href={ link('block', { id: String(data.block_number) }) }>
<LinkInternal fontWeight={ 700 } href={ route({ pathname: '/block/[height]', query: { height: String(data.block_number) } }) }>
<Box as={ shouldHighlightHash ? 'span' : 'mark' }>{ data.block_number }</Box>
</LinkInternal>
</Flex>
......
import { Tr, Td, Text, Flex, Icon, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
import type { SearchResultItem } from 'types/api/search';
......@@ -88,7 +89,7 @@ const SearchResultTableItem = ({ data, searchTerm }: Props) => {
<Td fontSize="sm">
<Flex alignItems="center">
<Icon as={ blockIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal fontWeight={ 700 } href={ link('block', { id: String(data.block_number) }) }>
<LinkInternal fontWeight={ 700 } href={ route({ pathname: '/block/[height]', query: { height: String(data.block_number) } }) }>
<Box as={ shouldHighlightHash ? 'span' : 'mark' }>{ data.block_number }</Box>
</LinkInternal>
</Flex>
......
import { chakra, shouldForwardProp, Tooltip, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import type { HTMLAttributeAnchorTarget } from 'react';
import React from 'react';
......@@ -27,7 +28,7 @@ type AddressTokenTxProps = {
type BlockProps = {
type: 'block';
hash: string;
id: string;
height: string;
}
type AddressTokenProps = {
......@@ -48,7 +49,7 @@ const AddressLink = (props: Props) => {
} else if (type === 'token') {
url = link('token_index', { hash: hash });
} else if (type === 'block') {
url = link('block', { id: props.id });
url = route({ pathname: '/block/[height]', query: { height: props.height } });
} else if (type === 'address_token') {
url = link('address_index', { id: hash }, { tab: 'token_transfers', token: props.tokenHash, scroll_to_tabs: 'true' });
} else {
......
import { Link, Icon, Text, HStack, Tooltip, Box } from '@chakra-ui/react';
import type { LinkProps } from 'next/link';
import NextLink from 'next/link';
import { route } from 'nextjs-routes';
import React from 'react';
import type { NavItem } from 'lib/hooks/useNavItems';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import useColors from './useColors';
interface Props {
type Props = NavItem & {
isCollapsed?: boolean;
isActive?: boolean;
url: string;
text: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
px?: string | number;
isNewUi: boolean;
}
const NavLink = ({ text, url, icon, isCollapsed, isActive, px, isNewUi }: Props) => {
const NavLink = ({ text, nextRoute, icon, isCollapsed, isActive, px, isNewUi }: Props) => {
const colors = useColors();
const isExpanded = isCollapsed === false;
const content = (
<Link
{ ...(isNewUi ? {} : { href: url }) }
{ ...(isNewUi ? {} : { href: route(nextRoute) }) }
w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }}
px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
py={ 2.5 }
......@@ -69,7 +65,7 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px, isNewUi }: Props)
{ /* why not NextLink in all cases? since prev UI and new one are hosting in the same domain and global routing is managed by nginx */ }
{ /* we have to hard reload page on every transition between urls from different part of the app */ }
{ isNewUi ? (
<NextLink href={ url as LinkProps['href'] } passHref>
<NextLink href={ nextRoute } passHref>
{ content }
</NextLink>
) : content }
......
import { chakra, Text, Flex, useColorModeValue, Icon, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
import type { SearchResultItem } from 'types/api/search';
......@@ -32,7 +33,7 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm }: Props) => {
return link('tx', { id: data.tx_hash });
}
case 'block': {
return link('block', { id: String(data.block_number) });
return route({ pathname: '/block/[height]', query: { height: String(data.block_number) } });
}
}
})();
......
......@@ -5,6 +5,7 @@ import {
Icon,
Text,
} from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Transaction } from 'types/api/transaction';
......@@ -14,7 +15,6 @@ import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -88,7 +88,7 @@ const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }:
{ showBlockInfo && tx.block !== null && (
<Box mt={ 2 }>
<Text as="span">Block </Text>
<LinkInternal href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</LinkInternal>
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: tx.block.toString() } }) }>{ tx.block }</LinkInternal>
</Box>
) }
<Flex alignItems="center" height={ 6 } mt={ 6 }>
......
......@@ -10,13 +10,13 @@ import {
Hide,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -99,7 +99,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }
</Td>
{ showBlockInfo && (
<Td>
{ tx.block && <LinkInternal href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</LinkInternal> }
{ tx.block && <LinkInternal href={ route({ pathname: '/block/[height]', query: { height: tx.block.toString() } }) }>{ tx.block }</LinkInternal> }
</Td>
) }
<Show above="xl" ssr={ false }>
......
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