Commit 550a89c4 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #593 from blockscout/no-link

no link() and better path params: pt1
parents 1016da40 9a66d3ac
...@@ -161,6 +161,26 @@ ...@@ -161,6 +161,26 @@
"instanceLimit": 1 "instanceLimit": 1
} }
}, },
{
"type": "shell",
"command": "npx playwright show-report",
"problemMatcher": [],
"label": "pw: report",
"detail": "serve test report",
"presentation": {
"reveal": "always",
"panel": "shared",
"close": true,
"revealProblems": "onProblem",
},
"icon": {
"color": "terminal.ansiBlue",
"id": "output"
},
"runOptions": {
"instanceLimit": 1
}
},
// JEST TESTS // JEST TESTS
{ {
......
...@@ -34,8 +34,8 @@ const oldUrls = [ ...@@ -34,8 +34,8 @@ const oldUrls = [
newPath: `${ PATHS.blocks }?tab=reorgs`, newPath: `${ PATHS.blocks }?tab=reorgs`,
}, },
{ {
oldPath: '/block/:id/transactions', oldPath: '/block/:height/transactions',
newPath: `${ PATHS.block }`, newPath: `${ PATHS.block }?tab=txs`,
}, },
{ {
oldPath: '/address/:id/transactions', oldPath: '/address/:id/transactions',
......
...@@ -97,10 +97,10 @@ export const RESOURCES = { ...@@ -97,10 +97,10 @@ export const RESOURCES = {
filterFields: [ 'type' as const ], filterFields: [ 'type' as const ],
}, },
block: { block: {
path: '/api/v2/blocks/:id', path: '/api/v2/blocks/:height',
}, },
block_txs: { 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 ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
filterFields: [], filterFields: [],
}, },
......
import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
...@@ -14,40 +16,76 @@ import tokensIcon from 'icons/token.svg'; ...@@ -14,40 +16,76 @@ import tokensIcon from 'icons/token.svg';
import transactionsIcon from 'icons/transactions.svg'; import transactionsIcon from 'icons/transactions.svg';
import walletIcon from 'icons/wallet.svg'; import walletIcon from 'icons/wallet.svg';
import watchlistIcon from 'icons/watchlist.svg'; import watchlistIcon from 'icons/watchlist.svg';
import link from 'lib/link/link';
import useCurrentRoute from 'lib/link/useCurrentRoute';
import notEmpty from 'lib/notEmpty'; 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 isMarketplaceFilled = appConfig.marketplaceAppList.length > 0;
const currentRoute = useCurrentRoute()(); const router = useRouter();
const pathname = router.pathname;
return React.useMemo(() => { return React.useMemo(() => {
const mainNavItems = [ const mainNavItems = [
{ text: 'Blocks', url: link('blocks'), icon: blocksIcon, isActive: currentRoute.startsWith('block'), isNewUi: true }, { text: 'Blocks', nextRoute: { pathname: '/blocks' as const }, icon: blocksIcon, isActive: pathname.startsWith('/block'), isNewUi: true },
{ text: 'Transactions', url: link('txs'), icon: transactionsIcon, isActive: currentRoute.startsWith('tx'), isNewUi: true }, { text: 'Transactions', nextRoute: { pathname: '/txs' as const }, icon: transactionsIcon, isActive: pathname.startsWith('/tx'), isNewUi: true },
{ text: 'Tokens', url: link('tokens'), icon: tokensIcon, isActive: currentRoute.startsWith('token'), isNewUi: true }, { text: 'Tokens', nextRoute: { pathname: '/tokens' as const }, icon: tokensIcon, isActive: pathname.startsWith('/token'), isNewUi: true },
{ text: 'Accounts', url: link('accounts'), icon: walletIcon, isActive: currentRoute === 'accounts', isNewUi: true }, { text: 'Accounts', nextRoute: { pathname: '/accounts' as const }, icon: walletIcon, isActive: pathname === '/accounts', isNewUi: true },
isMarketplaceFilled ? isMarketplaceFilled ?
{ text: 'Apps', url: link('apps'), icon: appsIcon, isActive: currentRoute.startsWith('app'), isNewUi: true } : null, { text: 'Apps', nextRoute: { pathname: '/apps' as const }, icon: appsIcon, isActive: pathname.startsWith('/app'), isNewUi: true } : null,
{ text: 'Charts & stats', url: link('stats'), icon: statsIcon, isActive: currentRoute === 'stats', isNewUi: true }, { 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' // 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/ // 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 // 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); ].filter(notEmpty);
const accountNavItems = [ 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: 'Watchlist',
{ text: 'Public tags', url: link('public_tags'), icon: publicTagIcon, isActive: currentRoute === 'public_tags', isNewUi: true }, nextRoute: { pathname: '/account/watchlist' as const },
{ text: 'API keys', url: link('api_keys'), icon: apiKeysIcon, isActive: currentRoute === 'api_keys', isNewUi: true }, icon: watchlistIcon,
{ text: 'Custom ABI', url: link('custom_abi'), icon: abiIcon, isActive: currentRoute === 'custom_abi', isNewUi: true }, 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 }; return { mainNavItems, accountNavItems, profileItem };
}, [ isMarketplaceFilled, currentRoute ]); }, [ isMarketplaceFilled, pathname ]);
} }
...@@ -5,6 +5,9 @@ import type { RouteName } from './routes'; ...@@ -5,6 +5,9 @@ import type { RouteName } from './routes';
const PATH_PARAM_REGEXP = /\/:(\w+)/g; const PATH_PARAM_REGEXP = /\/:(\w+)/g;
/**
* @deprecated Please consider to use "import { route } from 'nextjs-routes';" instead
*/
export default function link( export default function link(
routeName: RouteName, routeName: RouteName,
urlParams?: Record<string, Array<string> | string | undefined>, urlParams?: Record<string, Array<string> | string | undefined>,
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"txs": "/txs", "txs": "/txs",
"tx": "/tx/:id", "tx": "/tx/:id",
"blocks": "/blocks", "blocks": "/blocks",
"block": "/block/:id", "block": "/block/:height",
"tokens": "/tokens", "tokens": "/tokens",
"token_index": "/token/:hash", "token_index": "/token/:hash",
"token_instance_item": "/token/:hash/instance/:id", "token_instance_item": "/token/:hash/instance/:id",
......
...@@ -46,13 +46,13 @@ export const ROUTES = { ...@@ -46,13 +46,13 @@ export const ROUTES = {
}, },
// BLOCKS // BLOCKS
blocks: { // blocks: {
pattern: PATHS.blocks, // pattern: PATHS.blocks,
crossNetworkNavigation: true, // crossNetworkNavigation: true,
}, // },
block: { // block: {
pattern: PATHS.block, // pattern: PATHS.block,
}, // },
// TOKENS // TOKENS
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'; import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params?: PageParams) { export default function getSeo(params?: RoutedQuery<'/block/[height]'>) {
const networkTitle = getNetworkTitle(); const networkTitle = getNetworkTitle();
return { return {
title: params ? `Block ${ params.id } - ${ networkTitle }` : '', title: params ? `Block ${ params.height } - ${ networkTitle }` : '',
description: params ? `View the transactions, token transfers, and uncles for block number ${ params.id }` : '', 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 = { ...@@ -4,6 +4,7 @@ export type Props = {
cookies: string; cookies: string;
referrer: string; referrer: string;
id?: string; id?: string;
height?: string;
} }
export const getServerSideProps: GetServerSideProps<Props> = async({ req, query }) => { export const getServerSideProps: GetServerSideProps<Props> = async({ req, query }) => {
...@@ -12,6 +13,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, query ...@@ -12,6 +13,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, query
cookies: req.headers.cookie || '', cookies: req.headers.cookie || '',
referrer: req.headers.referer || '', referrer: req.headers.referer || '',
id: query.id?.toString() || '', 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 || '';
}
const withRoutes = require('nextjs-routes/config')({
outDir: 'types',
});
const path = require('path'); const path = require('path');
const headers = require('./configs/nextjs/headers'); const headers = require('./configs/nextjs/headers');
...@@ -33,4 +36,4 @@ const moduleExports = { ...@@ -33,4 +36,4 @@ const moduleExports = {
output: 'standalone', output: 'standalone',
}; };
module.exports = moduleExports; module.exports = withRoutes(moduleExports);
...@@ -16,7 +16,7 @@ const AppPage: NextPage = () => { ...@@ -16,7 +16,7 @@ const AppPage: NextPage = () => {
const [ isLoading, setIsLoading ] = useState(true); const [ isLoading, setIsLoading ] = useState(true);
const [ app, setApp ] = useState<AppItemOverview | undefined>(undefined); const [ app, setApp ] = useState<AppItemOverview | undefined>(undefined);
const { id }: { id?: string } = router.query; const id = router.query.id;
useEffect(() => { useEffect(() => {
if (!id) { if (!id) {
......
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { PageParams } from 'lib/next/block/types';
import getSeo from 'lib/next/block/getSeo'; import getSeo from 'lib/next/block/getSeo';
import Block from 'ui/pages/Block'; import Block from 'ui/pages/Block';
const BlockPage: NextPage<PageParams> = ({ id }: PageParams) => { const BlockPage: NextPage<RoutedQuery<'/block/[height]'>> = ({ height }: RoutedQuery<'/block/[height]'>) => {
const { title, description } = getSeo({ id }); const { title, description } = getSeo({ height });
return ( return (
<> <>
<Head> <Head>
......
import type { GetServerSideProps, NextPage } from 'next'; import type { GetServerSideProps, NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchRedirectResult } from 'types/api/search'; import type { SearchRedirectResult } from 'types/api/search';
...@@ -43,7 +44,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, r ...@@ -43,7 +44,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, r
const redirectUrl = (() => { const redirectUrl = (() => {
switch (payload.type) { switch (payload.type) {
case 'block': { case 'block': {
return link('block', { id: q }); return route({ pathname: '/block/[height]', query: { height: q } });
} }
case 'address': { case 'address': {
return link('address_index', { id: payload.parameter || q }); return link('address_index', { id: payload.parameter || q });
......
import './fonts.css'; import './fonts.css';
import { beforeMount } from '@playwright/experimental-ct-react/hooks'; import { beforeMount } from '@playwright/experimental-ct-react/hooks';
import _defaultsDeep from 'lodash/defaultsDeep';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
import * as router from 'next/router'; import * as router from 'next/router';
const NEXT_ROUTER_MOCK = { const NEXT_ROUTER_MOCK = {
query: {}, query: {},
pathname: '',
}; };
beforeMount(async({ hooksConfig }) => { beforeMount(async({ hooksConfig }) => {
// Before mount, redefine useRouter to return mock value from test. // Before mount, redefine useRouter to return mock value from test.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: I really want to redefine this property :) // @ts-ignore: I really want to redefine this property :)
router.useRouter = () => hooksConfig?.router || NEXT_ROUTER_MOCK; router.useRouter = () => _defaultsDeep(hooksConfig?.router, NEXT_ROUTER_MOCK);
// set current date // set current date
MockDate.set('2022-11-11T12:00:00Z'); MockDate.set('2022-11-11T12:00:00Z');
......
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// This file will be automatically regenerated when your Next.js server is running.
// nextjs-routes version: 1.0.8
/* eslint-disable */
// prettier-ignore
declare module "nextjs-routes" {
export type Route =
| StaticRoute<"/account/api_key">
| StaticRoute<"/account/custom_abi">
| StaticRoute<"/account/public_tags_request">
| StaticRoute<"/account/tag_address">
| StaticRoute<"/account/watchlist">
| StaticRoute<"/accounts">
| DynamicRoute<"/address/[id]/contract_verification", { "id": string }>
| DynamicRoute<"/address/[id]", { "id": string }>
| StaticRoute<"/api/csrf">
| StaticRoute<"/api/proxy">
| DynamicRoute<"/apps/[id]", { "id": string }>
| StaticRoute<"/apps">
| StaticRoute<"/auth/profile">
| DynamicRoute<"/block/[height]", { "height": string }>
| StaticRoute<"/blocks">
| StaticRoute<"/graph">
| StaticRoute<"/">
| StaticRoute<"/login">
| StaticRoute<"/search-results">
| StaticRoute<"/stats">
| DynamicRoute<"/token/[hash]", { "hash": string }>
| StaticRoute<"/tokens">
| DynamicRoute<"/tx/[id]", { "id": string }>
| StaticRoute<"/txs">
| StaticRoute<"/visualize/sol2uml">;
interface StaticRoute<Pathname> {
pathname: Pathname;
query?: Query | undefined;
hash?: string | null | undefined;
}
interface DynamicRoute<Pathname, Parameters> {
pathname: Pathname;
query: Parameters & Query;
hash?: string | null | undefined;
}
interface Query {
[key: string]: string | string[] | undefined;
};
export type RoutedQuery<P extends Route["pathname"]> = Extract<
Route,
{ pathname: P }
>["query"];
export type Locale = undefined;
/**
* A typesafe utility function for generating paths in your application.
*
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
*/
export declare function route(r: Route): string;
}
// prettier-ignore
declare module "next/link" {
import type { Route } from "nextjs-routes";
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
import type {
AnchorHTMLAttributes,
DetailedReactHTMLElement,
MouseEventHandler,
PropsWithChildren,
} from "react";
export * from "next/dist/client/link";
type Query = { query?: { [key: string]: string | string[] | undefined } };
type StaticRoute = Exclude<Route, { query: any }>["pathname"];
export interface LinkProps
extends Omit<NextLinkProps, "href" | "locale">,
AnchorHTMLAttributes<HTMLAnchorElement> {
href: Route | StaticRoute | Query;
locale?: false;
}
type LinkReactElement = DetailedReactHTMLElement<
{
onMouseEnter?: MouseEventHandler<Element> | undefined;
onClick: MouseEventHandler;
href?: string | undefined;
ref?: any;
},
HTMLElement
>;
declare function Link(props: PropsWithChildren<LinkProps>): LinkReactElement;
export default Link;
}
// prettier-ignore
declare module "next/router" {
import type { Locale, Route, RoutedQuery } from "nextjs-routes";
import type { NextRouter as Router } from "next/dist/client/router";
export * from "next/dist/client/router";
export { default } from "next/dist/client/router";
type NextTransitionOptions = NonNullable<Parameters<Router["push"]>[2]>;
type StaticRoute = Exclude<Route, { query: any }>["pathname"];
type Query = { query?: { [key: string]: string | string[] | undefined } };
interface TransitionOptions extends Omit<NextTransitionOptions, "locale"> {
locale?: false;
}
export type NextRouter<P extends Route["pathname"] = Route["pathname"]> =
Extract<Route, { pathname: P }> &
Omit<
Router,
| "push"
| "replace"
| "locale"
| "locales"
| "defaultLocale"
| "domainLocales"
> & {
defaultLocale?: undefined;
domainLocales?: undefined;
locale?: Locale;
locales?: undefined;
push(
url: Route | StaticRoute | Query,
as?: string,
options?: TransitionOptions
): Promise<boolean>;
replace(
url: Route | StaticRoute | Query,
as?: string,
options?: TransitionOptions
): Promise<boolean>;
route: P;
};
export function useRouter<P extends Route["pathname"]>(): NextRouter<P>;
}
import { Box, Flex, Text, Icon, Grid } from '@chakra-ui/react'; import { Box, Flex, Text, Icon, Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Address as TAddress } from 'types/api/address'; import type { Address as TAddress } from 'types/api/address';
...@@ -183,7 +184,7 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -183,7 +184,7 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
py={{ base: '2px', lg: 1 }} py={{ base: '2px', lg: 1 }}
> >
<LinkInternal <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" display="flex"
alignItems="center" alignItems="center"
> >
......
import { Text, Flex } from '@chakra-ui/react'; import { Text, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
...@@ -7,7 +8,6 @@ import type { Block } from 'types/api/block'; ...@@ -7,7 +8,6 @@ import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
...@@ -17,7 +17,7 @@ type Props = Block & { ...@@ -17,7 +17,7 @@ type Props = Block & {
}; };
const AddressBlocksValidatedListItem = (props: Props) => { const AddressBlocksValidatedListItem = (props: Props) => {
const blockUrl = link('block', { id: String(props.height) }); const blockUrl = route({ pathname: '/block/[height]', query: { height: props.height.toString() } });
const timeAgo = useTimeAgoIncrement(props.timestamp, props.page === 1); const timeAgo = useTimeAgoIncrement(props.timestamp, props.page === 1);
const totalReward = getBlockTotalReward(props); const totalReward = getBlockTotalReward(props);
......
import { Td, Tr, Text, Box, Flex } from '@chakra-ui/react'; import { Td, Tr, Text, Box, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
...@@ -15,7 +15,7 @@ type Props = Block & { ...@@ -15,7 +15,7 @@ type Props = Block & {
}; };
const AddressBlocksValidatedTableItem = (props: Props) => { 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 timeAgo = useTimeAgoIncrement(props.timestamp, props.page === 1);
const totalReward = getBlockTotalReward(props); const totalReward = getBlockTotalReward(props);
......
import { Text, Stat, StatHelpText, StatArrow, Flex } from '@chakra-ui/react'; import { Text, Stat, StatHelpText, StatArrow, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
...@@ -7,7 +8,6 @@ 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 appConfig from 'configs/app/config';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -18,7 +18,7 @@ type Props = AddressCoinBalanceHistoryItem & { ...@@ -18,7 +18,7 @@ type Props = AddressCoinBalanceHistoryItem & {
}; };
const AddressCoinBalanceListItem = (props: Props) => { 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 deltaBn = BigNumber(props.delta).div(WEI);
const isPositiveDelta = deltaBn.gte(ZERO); const isPositiveDelta = deltaBn.gte(ZERO);
const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1); const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1);
......
import { Td, Tr, Text, Stat, StatHelpText, StatArrow } from '@chakra-ui/react'; import { Td, Tr, Text, Stat, StatHelpText, StatArrow } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -16,7 +16,7 @@ type Props = AddressCoinBalanceHistoryItem & { ...@@ -16,7 +16,7 @@ type Props = AddressCoinBalanceHistoryItem & {
}; };
const AddressCoinBalanceTableItem = (props: Props) => { 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 deltaBn = BigNumber(props.delta).div(WEI);
const isPositiveDelta = deltaBn.gte(ZERO); const isPositiveDelta = deltaBn.gte(ZERO);
const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1); const timeAgo = useTimeAgoIncrement(props.block_timestamp, props.page === 1);
......
import { Flex, Tag, Icon, Box, HStack, Text } from '@chakra-ui/react'; import { Flex, Tag, Icon, Box, HStack, Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
...@@ -7,7 +8,6 @@ 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 appConfig from 'configs/app/config';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -50,7 +50,7 @@ const TxInternalsListItem = ({ ...@@ -50,7 +50,7 @@ const TxInternalsListItem = ({
</Flex> </Flex>
<HStack spacing={ 1 }> <HStack spacing={ 1 }>
<Text fontSize="sm" fontWeight={ 500 }>Block</Text> <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> </HStack>
<Box w="100%" display="flex" columnGap={ 3 }> <Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)"> <Address width="calc((100% - 48px) / 2)">
......
import { Tr, Td, Tag, Icon, Box, Flex, Text } from '@chakra-ui/react'; import { Tr, Td, Tag, Icon, Box, Flex, Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
...@@ -7,7 +8,6 @@ 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 appConfig from 'configs/app/config';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -58,7 +58,7 @@ const AddressIntTxsTableItem = ({ ...@@ -58,7 +58,7 @@ const AddressIntTxsTableItem = ({
</Flex> </Flex>
</Td> </Td>
<Td verticalAlign="middle"> <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>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%"> <Address display="inline-flex" maxW="100%">
......
...@@ -11,7 +11,6 @@ import type { Address } from 'types/api/address'; ...@@ -11,7 +11,6 @@ import type { Address } from 'types/api/address';
import walletIcon from 'icons/wallet.svg'; import walletIcon from 'icons/wallet.svg';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import link from 'lib/link/link';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -83,7 +82,7 @@ const TokenSelect = ({ onClick }: Props) => { ...@@ -83,7 +82,7 @@ const TokenSelect = ({ onClick }: Props) => {
} }
<Tooltip label="Show all tokens"> <Tooltip label="Show all tokens">
<Box> <Box>
<NextLink href={ link('address_index', { id: addressHash }, { tab: 'tokens' }) } passHref> <NextLink href={{ pathname: '/address/[id]', query: { id: addressHash || '', tab: 'tokens' } }} passHref>
<IconButton <IconButton
aria-label="Show all tokens" aria-label="Show all tokens"
variant="outline" variant="outline"
......
...@@ -2,8 +2,6 @@ import { LinkOverlay } from '@chakra-ui/react'; ...@@ -2,8 +2,6 @@ import { LinkOverlay } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React from 'react'; import React from 'react';
import link from 'lib/link/link';
type Props = { type Props = {
id: string; id: string;
url: string; url: string;
...@@ -17,7 +15,7 @@ const AppLink = ({ url, external, id, title }: Props) => { ...@@ -17,7 +15,7 @@ const AppLink = ({ url, external, id, title }: Props) => {
{ title } { title }
</LinkOverlay> </LinkOverlay>
) : ( ) : (
<NextLink href={ link('app_index', { id: id }) } passHref> <NextLink href={{ pathname: '/apps/[id]', query: { id } }} passHref>
<LinkOverlay> <LinkOverlay>
{ title } { title }
</LinkOverlay> </LinkOverlay>
......
...@@ -2,8 +2,6 @@ import { Button } from '@chakra-ui/react'; ...@@ -2,8 +2,6 @@ import { Button } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React from 'react'; import React from 'react';
import link from 'lib/link/link';
type Props = { type Props = {
id: string; id: string;
url: string; url: string;
...@@ -29,7 +27,7 @@ const AppModalLink = ({ url, external, id }: Props) => { ...@@ -29,7 +27,7 @@ const AppModalLink = ({ url, external, id }: Props) => {
{ ...buttonProps } { ...buttonProps }
>Launch app</Button> >Launch app</Button>
) : ( ) : (
<NextLink href={ link('app_index', { id: id }) } passHref> <NextLink href={{ pathname: '/apps/[id]', query: { id } }} passHref>
<Button <Button
as="a" as="a"
{ ...buttonProps } { ...buttonProps }
......
...@@ -7,10 +7,10 @@ import buildApiUrl from 'playwright/utils/buildApiUrl'; ...@@ -7,10 +7,10 @@ import buildApiUrl from 'playwright/utils/buildApiUrl';
import BlockDetails from './BlockDetails'; import BlockDetails from './BlockDetails';
const API_URL = buildApiUrl('block', { id: '1' }); const API_URL = buildApiUrl('block', { height: '1' });
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { id: '1' }, query: { height: '1' },
}, },
}; };
......
...@@ -2,6 +2,7 @@ import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react ...@@ -2,6 +2,7 @@ import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
...@@ -13,8 +14,8 @@ import getBlockReward from 'lib/block/getBlockReward'; ...@@ -13,8 +14,8 @@ import getBlockReward from 'lib/block/getBlockReward';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts'; import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import link from 'lib/link/link';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString';
import BlockDetailsSkeleton from 'ui/block/details/BlockDetailsSkeleton'; import BlockDetailsSkeleton from 'ui/block/details/BlockDetailsSkeleton';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
...@@ -30,10 +31,11 @@ import Utilization from 'ui/shared/Utilization/Utilization'; ...@@ -30,10 +31,11 @@ import Utilization from 'ui/shared/Utilization/Utilization';
const BlockDetails = () => { const BlockDetails = () => {
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
const router = useRouter(); const router = useRouter();
const height = getQueryParamString(router.query.height);
const { data, isLoading, isError, error } = useApiQuery('block', { const { data, isLoading, isError, error } = useApiQuery('block', {
pathParams: { id: router.query.id?.toString() }, pathParams: { height },
queryOptions: { enabled: Boolean(router.query.id) }, queryOptions: { enabled: Boolean(height) },
}); });
const handleCutClick = React.useCallback(() => { const handleCutClick = React.useCallback(() => {
...@@ -46,11 +48,10 @@ const BlockDetails = () => { ...@@ -46,11 +48,10 @@ const BlockDetails = () => {
const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => { const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
const increment = direction === 'next' ? +1 : -1; const increment = direction === 'next' ? +1 : -1;
const nextId = String(Number(router.query.id) + increment); const nextId = String(Number(height) + increment);
const url = link('block', { id: nextId }); router.push({ pathname: '/block/[height]', query: { height: nextId } }, undefined);
router.push(url, undefined); }, [ height, router ]);
}, [ router ]);
if (isLoading) { if (isLoading) {
return <BlockDetailsSkeleton/>; return <BlockDetailsSkeleton/>;
...@@ -116,7 +117,7 @@ const BlockDetails = () => { ...@@ -116,7 +117,7 @@ const BlockDetails = () => {
title="Transactions" title="Transactions"
hint="The number of transactions in the block." 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 { data.tx_count } transactions
</LinkInternal> </LinkInternal>
</DetailsInfoItem> </DetailsInfoItem>
......
import { Flex, Spinner, Text, Box, Icon } from '@chakra-ui/react'; import { Flex, Spinner, Text, Box, Icon } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
...@@ -9,7 +10,6 @@ import appConfig from 'configs/app/config'; ...@@ -9,7 +10,6 @@ import appConfig from 'configs/app/config';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import link from 'lib/link/link';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -36,7 +36,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -36,7 +36,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
{ isPending && <Spinner size="sm"/> } { isPending && <Spinner size="sm"/> }
<LinkInternal <LinkInternal
fontWeight={ 600 } fontWeight={ 600 }
href={ link('block', { id: String(data.height) }) } href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) }
> >
{ data.height } { data.height }
</LinkInternal> </LinkInternal>
...@@ -54,7 +54,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -54,7 +54,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text> <Text fontWeight={ 500 }>Txn</Text>
{ data.tx_count > 0 ? ( { 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 } { data.tx_count }
</LinkInternal> </LinkInternal>
) : ) :
......
import { Tr, Td, Flex, Box, Icon, Tooltip, Spinner, useColorModeValue } from '@chakra-ui/react'; import { Tr, Td, Flex, Box, Icon, Tooltip, Spinner, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
...@@ -8,7 +9,6 @@ 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 flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
...@@ -41,7 +41,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -41,7 +41,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations"> <Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations">
<LinkInternal <LinkInternal
fontWeight={ 600 } fontWeight={ 600 }
href={ link('block', { id: String(data.height) }) } href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) }
> >
{ data.height } { data.height }
</LinkInternal> </LinkInternal>
...@@ -55,7 +55,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -55,7 +55,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
</Td> </Td>
<Td isNumeric fontSize="sm"> <Td isNumeric fontSize="sm">
{ data.tx_count > 0 ? ( { 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 } { data.tx_count }
</LinkInternal> </LinkInternal>
) : data.tx_count } ) : data.tx_count }
......
...@@ -10,7 +10,6 @@ import type { SmartContractVerificationMethod, SmartContractVerificationConfig } ...@@ -10,7 +10,6 @@ import type { SmartContractVerificationMethod, SmartContractVerificationConfig }
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import link from 'lib/link/link';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -88,7 +87,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro ...@@ -88,7 +87,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
variant: 'subtle', variant: 'subtle',
isClosable: true, isClosable: true,
onCloseComplete: () => { onCloseComplete: () => {
router.push(link('address_index', { id: hash }, { tab: 'contract' }), undefined, { shallow: true }); router.push({ pathname: '/address/[id]', query: { id: hash, tab: 'contract' } }, undefined, { shallow: true });
}, },
}); });
}, [ hash, router, setError, toast ]); }, [ hash, router, setError, toast ]);
......
import { Box, Heading, Flex, Text, VStack, Skeleton } from '@chakra-ui/react'; import { Box, Heading, Flex, Text, VStack, Skeleton } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
...@@ -9,7 +10,6 @@ import type { Block } from 'types/api/block'; ...@@ -9,7 +10,6 @@ import type { Block } from 'types/api/block';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import link from 'lib/link/link';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -98,7 +98,7 @@ const LatestBlocks = () => { ...@@ -98,7 +98,7 @@ const LatestBlocks = () => {
</AnimatePresence> </AnimatePresence>
</VStack> </VStack>
<Flex justifyContent="center"> <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> </Flex>
</> </>
); );
......
...@@ -8,13 +8,13 @@ import { ...@@ -8,13 +8,13 @@ import {
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -44,7 +44,7 @@ const LatestBlocksItem = ({ block, h }: Props) => { ...@@ -44,7 +44,7 @@ const LatestBlocksItem = ({ block, h }: Props) => {
<HStack spacing={ 2 }> <HStack spacing={ 2 }>
<Icon as={ blockIcon } boxSize="30px" color="link"/> <Icon as={ blockIcon } boxSize="30px" color="link"/>
<LinkInternal <LinkInternal
href={ link('block', { id: String(block.height) }) } href={ route({ pathname: '/block/[height]', query: { height: String(block.height) } }) }
fontSize="xl" fontSize="xl"
fontWeight="500" fontWeight="500"
> >
......
import { Grid } from '@chakra-ui/react'; import { Grid } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
...@@ -45,7 +46,7 @@ const Stats = () => { ...@@ -45,7 +46,7 @@ const Stats = () => {
icon={ blockIcon } icon={ blockIcon }
title="Total blocks" title="Total blocks"
value={ Number(data.total_blocks).toLocaleString() } value={ Number(data.total_blocks).toLocaleString() }
url={ link('blocks') } url={ route({ pathname: '/blocks' }) }
/> />
{ hasAvgBlockTime && ( { hasAvgBlockTime && (
<StatsItem <StatsItem
......
...@@ -6,6 +6,7 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; ...@@ -6,6 +6,7 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString';
import BlockDetails from 'ui/block/BlockDetails'; import BlockDetails from 'ui/block/BlockDetails';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
...@@ -24,17 +25,19 @@ const BlockPageContent = () => { ...@@ -24,17 +25,19 @@ const BlockPageContent = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const appProps = useAppContext(); const appProps = useAppContext();
const height = getQueryParamString(router.query.height);
const tab = getQueryParamString(router.query.tab);
const blockTxsQuery = useQueryWithPages({ const blockTxsQuery = useQueryWithPages({
resourceName: 'block_txs', resourceName: 'block_txs',
pathParams: { id: router.query.id?.toString() }, pathParams: { height },
options: { options: {
enabled: Boolean(router.query.id && router.query.tab === 'txs'), enabled: Boolean(height && tab === 'txs'),
}, },
}); });
if (!router.query.id) { if (!height) {
return null; throw new Error('Block not found', { cause: { status: 404 } });
} }
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
...@@ -42,7 +45,7 @@ const BlockPageContent = () => { ...@@ -42,7 +45,7 @@ const BlockPageContent = () => {
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> }, { 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'); const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/blocks');
...@@ -50,7 +53,7 @@ const BlockPageContent = () => { ...@@ -50,7 +53,7 @@ const BlockPageContent = () => {
<Page> <Page>
<TextAd mb={ 6 }/> <TextAd mb={ 6 }/>
<PageTitle <PageTitle
text={ `Block #${ router.query.id }` } text={ `Block #${ height }` }
backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined } backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined }
backLinkLabel="Back to blocks list" backLinkLabel="Back to blocks list"
/> />
......
...@@ -7,7 +7,7 @@ import type { SmartContractVerificationConfigRaw, SmartContractVerificationMetho ...@@ -7,7 +7,7 @@ import type { SmartContractVerificationConfigRaw, SmartContractVerificationMetho
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
import link from 'lib/link/link'; import getQueryParamString from 'lib/router/getQueryParamString';
import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm'; import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm';
import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils'; import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -26,8 +26,8 @@ const ContractVerification = () => { ...@@ -26,8 +26,8 @@ const ContractVerification = () => {
const hasGoBackLink = referrer && referrer.includes('/address'); const hasGoBackLink = referrer && referrer.includes('/address');
const router = useRouter(); const router = useRouter();
const hash = router.query.id?.toString(); const hash = getQueryParamString(router.query.id);
const method = router.query.id?.toString() as SmartContractVerificationMethod | undefined; const method = getQueryParamString(router.query.method) as SmartContractVerificationMethod;
const contractQuery = useApiQuery('contract', { const contractQuery = useApiQuery('contract', {
pathParams: { id: hash }, pathParams: { id: hash },
...@@ -55,7 +55,7 @@ const ContractVerification = () => { ...@@ -55,7 +55,7 @@ const ContractVerification = () => {
React.useEffect(() => { React.useEffect(() => {
if (method && hash) { if (method && hash) {
router.replace(link('address_contract_verification', { id: hash }), undefined, { scroll: false, shallow: true }); router.replace({ pathname: '/address/[id]/contract_verification', query: { id: hash } }, undefined, { scroll: false, shallow: true });
} }
// onMount only // onMount only
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
...@@ -65,7 +65,7 @@ const ContractVerification = () => { ...@@ -65,7 +65,7 @@ const ContractVerification = () => {
React.useEffect(() => { React.useEffect(() => {
if (isVerifiedContract) { if (isVerifiedContract) {
router.push(link('address_index', { id: hash }, { tab: 'contract' }), undefined, { scroll: false, shallow: true }); router.push({ pathname: '/address/[id]', query: { id: hash, tab: 'contract' } }, undefined, { scroll: false, shallow: true });
} }
}, [ hash, isVerifiedContract, router ]); }, [ hash, isVerifiedContract, router ]);
......
import { Text, Flex, Icon, Box, chakra } from '@chakra-ui/react'; import { Text, Flex, Icon, Box, chakra } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
...@@ -56,7 +57,7 @@ const SearchResultListItem = ({ data, searchTerm }: Props) => { ...@@ -56,7 +57,7 @@ const SearchResultListItem = ({ data, searchTerm }: Props) => {
return ( return (
<Flex alignItems="center"> <Flex alignItems="center">
<Icon as={ blockIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/> <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> <Box as={ shouldHighlightHash ? 'span' : 'mark' }>{ data.block_number }</Box>
</LinkInternal> </LinkInternal>
</Flex> </Flex>
......
import { Tr, Td, Text, Flex, Icon, Box } from '@chakra-ui/react'; import { Tr, Td, Text, Flex, Icon, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
...@@ -88,7 +89,7 @@ const SearchResultTableItem = ({ data, searchTerm }: Props) => { ...@@ -88,7 +89,7 @@ const SearchResultTableItem = ({ data, searchTerm }: Props) => {
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center"> <Flex alignItems="center">
<Icon as={ blockIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/> <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> <Box as={ shouldHighlightHash ? 'span' : 'mark' }>{ data.block_number }</Box>
</LinkInternal> </LinkInternal>
</Flex> </Flex>
......
import type { LinkProps } from '@chakra-ui/react'; import type { LinkProps } from '@chakra-ui/react';
import { Link } from '@chakra-ui/react'; import { Link } from '@chakra-ui/react';
import type { LinkProps as NextLinkProps } from 'next/link';
import NextLink from 'next/link'; import NextLink from 'next/link';
import type { LegacyRef } from 'react'; import type { LegacyRef } from 'react';
import React from 'react'; import React from 'react';
...@@ -11,7 +12,7 @@ const LinkInternal = (props: LinkProps, ref: LegacyRef<HTMLAnchorElement>) => { ...@@ -11,7 +12,7 @@ const LinkInternal = (props: LinkProps, ref: LegacyRef<HTMLAnchorElement>) => {
} }
return ( return (
<NextLink href={ props.href } passHref target={ props.target }> <NextLink href={ props.href as NextLinkProps['href'] } passHref target={ props.target }>
<Link { ...props } ref={ ref }/> <Link { ...props } ref={ ref }/>
</NextLink> </NextLink>
); );
......
...@@ -52,7 +52,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, . ...@@ -52,7 +52,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, .
const nextTab = tabs[index]; const nextTab = tabs[index];
router.push( router.push(
{ pathname: router.asPath.split('?')[0], query: { tab: nextTab.id } }, { pathname: router.asPath.split('?')[0], query: { tab: nextTab.id } } as Parameters<typeof router.push>[0],
undefined, undefined,
{ shallow: true }, { shallow: true },
); );
......
import { chakra, shouldForwardProp, Tooltip, Box } from '@chakra-ui/react'; import { chakra, shouldForwardProp, Tooltip, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import type { HTMLAttributeAnchorTarget } from 'react'; import type { HTMLAttributeAnchorTarget } from 'react';
import React from 'react'; import React from 'react';
...@@ -27,7 +28,7 @@ type AddressTokenTxProps = { ...@@ -27,7 +28,7 @@ type AddressTokenTxProps = {
type BlockProps = { type BlockProps = {
type: 'block'; type: 'block';
hash: string; hash: string;
id: string; height: string;
} }
type AddressTokenProps = { type AddressTokenProps = {
...@@ -48,7 +49,7 @@ const AddressLink = (props: Props) => { ...@@ -48,7 +49,7 @@ const AddressLink = (props: Props) => {
} else if (type === 'token') { } else if (type === 'token') {
url = link('token_index', { hash: hash }); url = link('token_index', { hash: hash });
} else if (type === 'block') { } else if (type === 'block') {
url = link('block', { id: props.id }); url = route({ pathname: '/block/[height]', query: { height: props.height } });
} else if (type === 'address_token') { } else if (type === 'address_token') {
url = link('address_index', { id: hash }, { tab: 'token_transfers', token: props.tokenHash, scroll_to_tabs: 'true' }); url = link('address_index', { id: hash }, { tab: 'token_transfers', token: props.tokenHash, scroll_to_tabs: 'true' });
} else { } else {
......
import { Link, Icon, Text, HStack, Tooltip, Box } from '@chakra-ui/react'; import { Link, Icon, Text, HStack, Tooltip, Box } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { NavItem } from 'lib/hooks/useNavItems';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import useColors from './useColors'; import useColors from './useColors';
interface Props { type Props = NavItem & {
isCollapsed?: boolean; isCollapsed?: boolean;
isActive?: boolean;
url: string;
text: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
px?: string | number; 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 colors = useColors();
const isExpanded = isCollapsed === false; const isExpanded = isCollapsed === false;
const content = ( const content = (
<Link <Link
{ ...(isNewUi ? {} : { href: url }) } { ...(isNewUi ? {} : { href: route(nextRoute) }) }
w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }} w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }}
px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } } px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
py={ 2.5 } py={ 2.5 }
...@@ -68,7 +65,7 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px, isNewUi }: Props) ...@@ -68,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 */ } { /* 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 */ } { /* we have to hard reload page on every transition between urls from different part of the app */ }
{ isNewUi ? ( { isNewUi ? (
<NextLink href={ url } passHref> <NextLink href={ nextRoute } passHref>
{ content } { content }
</NextLink> </NextLink>
) : content } ) : content }
......
...@@ -12,6 +12,7 @@ const hooksConfig = { ...@@ -12,6 +12,7 @@ const hooksConfig = {
router: { router: {
route: '/blocks', route: '/blocks',
query: { id: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' }, query: { id: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' },
pathname: '/blocks',
}, },
}; };
......
...@@ -12,6 +12,7 @@ test('no auth', async({ mount, page }) => { ...@@ -12,6 +12,7 @@ test('no auth', async({ mount, page }) => {
const hooksConfig = { const hooksConfig = {
router: { router: {
asPath: '/', asPath: '/',
pathname: '/',
}, },
}; };
const component = await mount( const component = await mount(
......
import { chakra, Text, Flex, useColorModeValue, Icon, Box } from '@chakra-ui/react'; import { chakra, Text, Flex, useColorModeValue, Icon, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
...@@ -32,7 +33,7 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm }: Props) => { ...@@ -32,7 +33,7 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm }: Props) => {
return link('tx', { id: data.tx_hash }); return link('tx', { id: data.tx_hash });
} }
case 'block': { case 'block': {
return link('block', { id: String(data.block_number) }); return route({ pathname: '/block/[height]', query: { height: String(data.block_number) } });
} }
} }
})(); })();
......
...@@ -8,7 +8,6 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -8,7 +8,6 @@ import type { TokenInfo } from 'types/api/token';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import link from 'lib/link/link';
import type { TokenTabs } from 'ui/pages/Token'; import type { TokenTabs } from 'ui/pages/Token';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow'; import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
...@@ -26,9 +25,8 @@ const TokenDetails = ({ tokenQuery }: Props) => { ...@@ -26,9 +25,8 @@ const TokenDetails = ({ tokenQuery }: Props) => {
}); });
const changeUrlAndScroll = useCallback((tab: TokenTabs) => () => { const changeUrlAndScroll = useCallback((tab: TokenTabs) => () => {
const newLink = link('token_index', { hash: router.query.hash }, { tab: tab });
router.push( router.push(
newLink, { pathname: '/token/[hash]', query: { hash: router.query.hash?.toString() || '', tab } },
undefined, undefined,
{ shallow: true }, { shallow: true },
); );
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
Icon, Icon,
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
...@@ -14,7 +15,6 @@ import rightArrowIcon from 'icons/arrows/east.svg'; ...@@ -14,7 +15,6 @@ import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -88,7 +88,7 @@ const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }: ...@@ -88,7 +88,7 @@ const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }:
{ showBlockInfo && tx.block !== null && ( { showBlockInfo && tx.block !== null && (
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Block </Text> <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> </Box>
) } ) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
......
...@@ -10,13 +10,13 @@ import { ...@@ -10,13 +10,13 @@ import {
Hide, Hide,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -99,7 +99,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement } ...@@ -99,7 +99,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }
</Td> </Td>
{ showBlockInfo && ( { showBlockInfo && (
<Td> <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> </Td>
) } ) }
<Show above="xl" ssr={ false }> <Show above="xl" ssr={ false }>
......
...@@ -4897,6 +4897,14 @@ anymatch@^3.0.3: ...@@ -4897,6 +4897,14 @@ anymatch@^3.0.3:
normalize-path "^3.0.0" normalize-path "^3.0.0"
picomatch "^2.0.4" picomatch "^2.0.4"
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
aproba@^1.0.3: aproba@^1.0.3:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
...@@ -5161,6 +5169,11 @@ bignumber.js@^9.1.0: ...@@ -5161,6 +5169,11 @@ bignumber.js@^9.1.0:
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62"
integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bind-decorator@^1.0.11: bind-decorator@^1.0.11:
version "1.0.11" version "1.0.11"
resolved "https://registry.yarnpkg.com/bind-decorator/-/bind-decorator-1.0.11.tgz#e41bc06a1f65dd9cec476c91c5daf3978488252f" resolved "https://registry.yarnpkg.com/bind-decorator/-/bind-decorator-1.0.11.tgz#e41bc06a1f65dd9cec476c91c5daf3978488252f"
...@@ -5222,7 +5235,7 @@ brace-expansion@^2.0.1: ...@@ -5222,7 +5235,7 @@ brace-expansion@^2.0.1:
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
braces@^3.0.2: braces@^3.0.2, braces@~3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
...@@ -5415,6 +5428,21 @@ char-regex@^1.0.2: ...@@ -5415,6 +5428,21 @@ char-regex@^1.0.2:
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
ci-info@^3.2.0: ci-info@^3.2.0:
version "3.6.1" version "3.6.1"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.6.1.tgz#7594f1c95cb7fdfddee7af95a13af7dbc67afdcf" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.6.1.tgz#7594f1c95cb7fdfddee7af95a13af7dbc67afdcf"
...@@ -7421,7 +7449,7 @@ get-symbol-description@^1.0.0: ...@@ -7421,7 +7449,7 @@ get-symbol-description@^1.0.0:
call-bind "^1.0.2" call-bind "^1.0.2"
get-intrinsic "^1.1.1" get-intrinsic "^1.1.1"
glob-parent@^5.1.2: glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
...@@ -7778,6 +7806,13 @@ is-bigint@^1.0.1: ...@@ -7778,6 +7806,13 @@ is-bigint@^1.0.1:
dependencies: dependencies:
has-bigints "^1.0.1" has-bigints "^1.0.1"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.1.0: is-boolean-object@^1.1.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
...@@ -7844,7 +7879,7 @@ is-generator-function@^1.0.7: ...@@ -7844,7 +7879,7 @@ is-generator-function@^1.0.7:
dependencies: dependencies:
has-tostringtag "^1.0.0" has-tostringtag "^1.0.0"
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
...@@ -9039,6 +9074,13 @@ next@12.2.5: ...@@ -9039,6 +9074,13 @@ next@12.2.5:
"@next/swc-win32-ia32-msvc" "12.2.5" "@next/swc-win32-ia32-msvc" "12.2.5"
"@next/swc-win32-x64-msvc" "12.2.5" "@next/swc-win32-x64-msvc" "12.2.5"
nextjs-routes@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/nextjs-routes/-/nextjs-routes-1.0.8.tgz#7604fe12dd0132c5a4c61e8d4a6c00c19cf615fb"
integrity sha512-EWBrzT0VS+SGcrhEbCUGnDlEAS6tjJSiecbSA7x75pBbcW9IYkYw7JxDMNqFhiSrvtYAL8yIWFwbQ8x0Jmz7wg==
dependencies:
chokidar "^3.5.3"
node-addon-api@^2.0.0: node-addon-api@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
...@@ -9080,7 +9122,7 @@ node-releases@^2.0.6: ...@@ -9080,7 +9122,7 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
normalize-path@^3.0.0: normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
...@@ -9386,7 +9428,7 @@ picocolors@^1.0.0: ...@@ -9386,7 +9428,7 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
...@@ -9931,6 +9973,13 @@ readable-stream@^4.0.0: ...@@ -9931,6 +9973,13 @@ readable-stream@^4.0.0:
events "^3.3.0" events "^3.3.0"
process "^0.11.10" process "^0.11.10"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
real-require@^0.1.0: real-require@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381"
......
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