Commit c51631f0 authored by tom's avatar tom

skeletons for L2 deposits

parent 60463634
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import L2Deposits from 'ui/pages/L2Deposits';
import Page from 'ui/shared/Page/Page';
const L2Deposits = dynamic(() => import('ui/pages/L2Deposits'), { ssr: false });
const DepositsPage: NextPage = () => {
const title = getNetworkTitle();
......@@ -12,7 +15,9 @@ const DepositsPage: NextPage = () => {
<Head>
<title>{ title }</title>
</Head>
<L2Deposits/>
<Page>
<L2Deposits/>
</Page>
</>
);
};
......
import type { L2DepositsItem } from 'types/api/l2Deposits';
import { ADDRESS_HASH } from './addressParams';
import { TX_HASH } from './tx';
export const L2_DEPOSIT_ITEM: L2DepositsItem = {
l1_block_number: 9045233,
l1_block_timestamp: '2023-05-22T18:00:36.000000Z',
l1_tx_hash: TX_HASH,
l1_tx_origin: ADDRESS_HASH,
l2_tx_gas_limit: '100000',
l2_tx_hash: TX_HASH,
};
......@@ -14,5 +14,4 @@ export type L2DepositsResponse = {
l1_block_number: number;
tx_hash: string;
};
total: number;
}
import { Box, Icon } from '@chakra-ui/react';
import { Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
......@@ -10,33 +10,35 @@ import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon';
import Icon from 'ui/shared/chakra/Icon';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
type Props = { item: L2DepositsItem };
type Props = { item: L2DepositsItem; isLoading?: boolean };
const DepositsListItem = ({ item }: Props) => {
const DepositsListItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_block_timestamp).fromNow();
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label>L1 block No</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) }
fontWeight={ 600 }
display="inline-flex"
isLoading={ isLoading }
>
<Icon as={ blockIcon } boxSize={ 6 } mr={ 1 }/>
{ item.l1_block_number }
<Icon as={ blockIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } ml={ 1 }>{ item.l1_block_number }</Skeleton>
</LinkExternal>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<LinkInternal
href={ route({ pathname: '/tx/[hash]', query: { hash: item.l2_tx_hash } }) }
display="flex"
......@@ -44,43 +46,56 @@ const DepositsListItem = ({ item }: Props) => {
alignItems="center"
overflow="hidden"
w="100%"
isLoading={ isLoading }
>
<Icon as={ txIcon } boxSize={ 6 } mr={ 1 }/>
<Box w="calc(100% - 36px)" overflow="hidden" whiteSpace="nowrap"><HashStringShortenDynamic hash={ item.l2_tx_hash }/></Box>
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 36px)" overflow="hidden" whiteSpace="nowrap" ml={ 1 }>
<HashStringShortenDynamic hash={ item.l2_tx_hash }/>
</Skeleton>
</LinkInternal>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>{ timeAgo }</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Label isLoading={ isLoading }>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ timeAgo }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
maxW="100%"
display="inline-flex"
overflow="hidden"
isLoading={ isLoading }
>
<Icon as={ txIcon } boxSize={ 6 } mr={ 1 }/>
<Box w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap"><HashStringShortenDynamic hash={ item.l1_tx_hash }/></Box>
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 1 }>
<HashStringShortenDynamic hash={ item.l1_tx_hash }/>
</Skeleton>
</LinkExternal>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label>L1 txn origin</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) }
maxW="100%"
display="inline-flex"
overflow="hidden"
isLoading={ isLoading }
>
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} mr={ 2 }/>
<Box w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap"><HashStringShortenDynamic hash={ item.l1_tx_origin }/></Box>
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 2 }>
<HashStringShortenDynamic hash={ item.l1_tx_origin }/>
</Skeleton>
</LinkExternal>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label>Gas limit</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>{ BigNumber(item.l2_tx_gas_limit).toFormat() }</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Gas limit</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(item.l2_tx_gas_limit).toFormat() }</Skeleton>
</ListItemMobileGrid.Value>
</ListItemMobileGrid.Container>
);
......
......@@ -10,9 +10,10 @@ import DepositsTableItem from './DepositsTableItem';
type Props = {
items: Array<L2DepositsItem>;
top: number;
isLoading?: boolean;
}
const DepositsTable = ({ items, top }: Props) => {
const DepositsTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
......@@ -26,8 +27,8 @@ const DepositsTable = ({ items, top }: Props) => {
</Tr>
</Thead>
<Tbody>
{ items.map((item) => (
<DepositsTableItem key={ item.l2_tx_hash } item={ item }/>
{ items.map((item, index) => (
<DepositsTableItem key={ item.l2_tx_hash + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
......
import { Box, Td, Tr, Text, Icon } from '@chakra-ui/react';
import { Td, Tr, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react';
......@@ -10,13 +10,14 @@ import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon';
import Icon from 'ui/shared/chakra/Icon';
import HashStringShorten from 'ui/shared/HashStringShorten';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
type Props = { item: L2DepositsItem };
type Props = { item: L2DepositsItem; isLoading?: boolean };
const WithdrawalsTableItem = ({ item }: Props) => {
const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_block_timestamp).fromNow();
return (
......@@ -26,9 +27,12 @@ const WithdrawalsTableItem = ({ item }: Props) => {
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) }
fontWeight={ 600 }
display="inline-flex"
isLoading={ isLoading }
>
<Icon as={ blockIcon } boxSize={ 6 } mr={ 1 }/>
{ item.l1_block_number }
<Icon as={ blockIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } ml={ 1 }>
{ item.l1_block_number }
</Skeleton>
</LinkExternal>
</Td>
<Td verticalAlign="middle">
......@@ -39,13 +43,16 @@ const WithdrawalsTableItem = ({ item }: Props) => {
alignItems="center"
overflow="hidden"
w="100%"
isLoading={ isLoading }
>
<Icon as={ txIcon } boxSize={ 6 } mr={ 1 }/>
<Box w="calc(100% - 36px)" overflow="hidden" whiteSpace="nowrap"><HashStringShorten hash={ item.l2_tx_hash }/></Box>
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 36px)" ml={ 1 } overflow="hidden" whiteSpace="nowrap">
<HashStringShorten hash={ item.l2_tx_hash }/>
</Skeleton>
</LinkInternal>
</Td>
<Td verticalAlign="middle" pr={ 12 }>
<Text variant="secondary">{ timeAgo }</Text>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block"><span>{ timeAgo }</span></Skeleton>
</Td>
<Td verticalAlign="middle">
<LinkExternal
......@@ -53,9 +60,12 @@ const WithdrawalsTableItem = ({ item }: Props) => {
maxW="100%"
display="inline-flex"
overflow="hidden"
isLoading={ isLoading }
>
<Icon as={ txIcon } boxSize={ 6 } mr={ 1 }/>
<Box w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap"><HashStringShorten hash={ item.l1_tx_hash }/></Box>
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 1 }>
<HashStringShorten hash={ item.l1_tx_hash }/>
</Skeleton>
</LinkExternal>
</Td>
<Td verticalAlign="middle">
......@@ -64,13 +74,18 @@ const WithdrawalsTableItem = ({ item }: Props) => {
maxW="100%"
display="inline-flex"
overflow="hidden"
isLoading={ isLoading }
>
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} mr={ 2 }/>
<Box w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap"><HashStringShorten hash={ item.l1_tx_origin }/></Box>
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 2 }>
<HashStringShorten hash={ item.l1_tx_origin }/>
</Skeleton>
</LinkExternal>
</Td>
<Td verticalAlign="middle" isNumeric>
<Text variant="secondary">{ BigNumber(item.l2_tx_gas_limit).toFormat() }</Text>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block">
<span>{ BigNumber(item.l2_tx_gas_limit).toFormat() }</span>
</Skeleton>
</Td>
</Tr>
);
......
import { Flex, Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import { Box, Hide, Show, Skeleton } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import { L2_DEPOSIT_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import DepositsListItem from 'ui/l2Deposits/DepositsListItem';
import DepositsTable from 'ui/l2Deposits/DepositsTable';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
const L2Deposits = () => {
const isMobile = useIsMobile();
const { data, isError, isLoading, isPaginationVisible, pagination } = useQueryWithPages({
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({
resourceName: 'l2_deposits',
options: {
placeholderData: generateListStub<'l2_deposits'>(
L2_DEPOSIT_ITEM,
50,
{
next_page_params: {
items_count: 50,
l1_block_number: 9045200,
tx_hash: '',
},
},
),
},
});
const countersQuery = useApiQuery('l2_deposits_count');
const countersQuery = useApiQuery('l2_deposits_count', {
queryOptions: {
placeholderData: 1927029,
},
});
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>{ data.items.map((item => <DepositsListItem key={ item.l2_tx_hash } item={ item }/>)) }</Show>
<Hide below="lg" ssr={ false }><DepositsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 }/></Hide>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<DepositsListItem
key={ item.l2_tx_hash + (isPlaceholderData ? index : '') }
isLoading={ isPlaceholderData }
item={ item }
/>
))) }
</Show>
<Hide below="lg" ssr={ false }>
<DepositsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
const text = (() => {
if (countersQuery.isLoading) {
return (
<Skeleton
w={{ base: '100%', lg: '320px' }}
h="24px"
mb={{ base: 7, lg: isPaginationVisible ? 0 : 7 }}
mt={{ base: 1, lg: isPaginationVisible ? 0 : 7 }}
/>
);
}
if (countersQuery.isError) {
return null;
}
return (
<Text mb={{ base: 6, lg: isPaginationVisible ? 0 : 6 }} lineHeight={{ base: '24px', lg: '32px' }}>
A total of { countersQuery.data.toLocaleString() } deposits found
</Text>
<Skeleton
isLoaded={ !countersQuery.isPlaceholderData }
display="inline-block"
>
A total of { countersQuery.data?.toLocaleString() } deposits found
</Skeleton>
);
})();
const actionBar = (
<>
{ (isMobile || !isPaginationVisible) && text }
{ isPaginationVisible && (
<ActionBar mt={ -6 }>
<Flex alignItems="center" justifyContent="space-between" w="100%">
{ !isMobile && text }
<Pagination ml="auto" { ...pagination }/>
</Flex>
</ActionBar>
) }
<Box mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ text }
</Box>
<ActionBar mt={ -6 }>
<Box display={{ base: 'none', lg: 'flex' }}>
{ text }
</Box>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> }
</ActionBar>
</>
);
return (
<Page>
<>
<PageTitle title={ `Deposits (L1${ nbsp }${ rightLineArrow }${ nbsp }L2)` } withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
isLoading={ false }
items={ data?.items }
skeletonProps={{ skeletonDesktopColumns: Array(7).fill(`${ 100 / 7 }%`), skeletonDesktopMinW: '950px' }}
emptyText="There are no withdrawals."
content={ content }
actionBar={ actionBar }
/>
</Page>
</>
);
};
......
import { Link, Icon, chakra } from '@chakra-ui/react';
import { Link, Icon, chakra, Box, Skeleton } from '@chakra-ui/react';
import React from 'react';
import arrowIcon from 'icons/arrows/north-east.svg';
......@@ -7,9 +7,19 @@ interface Props {
href: string;
className?: string;
children: React.ReactNode;
isLoading?: boolean;
}
const LinkExternal = ({ href, children, className }: Props) => {
const LinkExternal = ({ href, children, className, isLoading }: Props) => {
if (isLoading) {
return (
<Box className={ className } fontSize="sm" lineHeight={ 5 } display="inline-block" alignItems="center">
{ children }
<Skeleton boxSize={ 4 } verticalAlign="middle"/>
</Box>
);
}
return (
<Link className={ className } fontSize="sm" lineHeight={ 5 } display="inline-block" alignItems="center" target="_blank" href={ href }>
{ children }
......
import type { LinkProps } from '@chakra-ui/react';
import { Link } from '@chakra-ui/react';
import { Flex, Link } from '@chakra-ui/react';
import type { LinkProps as NextLinkProps } from 'next/link';
import NextLink from 'next/link';
import type { LegacyRef } from 'react';
import React from 'react';
// NOTE! use this component only for links to pages that are completely implemented in new UI
const LinkInternal = (props: LinkProps, ref: LegacyRef<HTMLAnchorElement>) => {
const LinkInternal = (props: LinkProps & { isLoading?: boolean }, ref: LegacyRef<HTMLAnchorElement>) => {
if (props.isLoading) {
return <Flex alignItems="center">{ props.children }</Flex>;
}
if (!props.href) {
return <Link { ...props } ref={ ref }/>;
}
......
......@@ -20,7 +20,8 @@ const Container = chakra(({ isAnimated, children, className }: ContainerProps) =
rowGap={ 2 }
columnGap={ 2 }
gridTemplateColumns="86px auto"
gridTemplateRows="minmax(30px, max-content)"
gridTemplateRows="30px"
alignItems="start"
paddingY={ 4 }
borderColor="divider"
borderTopWidth="1px"
......@@ -29,6 +30,7 @@ const Container = chakra(({ isAnimated, children, className }: ContainerProps) =
}}
className={ className }
fontSize="sm"
lineHeight="20px"
>
{ children }
</Grid>
......@@ -47,7 +49,6 @@ const Label = chakra(({ children, className, isLoading }: LabelProps) => {
className={ className }
isLoaded={ !isLoading }
fontWeight={ 500 }
lineHeight="20px"
my="5px"
justifySelf="start"
>
......
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