Commit de5dcd73 authored by tom's avatar tom

internal txs tab

parent 81bf03c8
...@@ -8,7 +8,7 @@ import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; ...@@ -8,7 +8,7 @@ import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
export interface BadgeProps extends ChakraBadgeProps { export interface BadgeProps extends Omit<ChakraBadgeProps, 'colorScheme'> {
loading?: boolean; loading?: boolean;
iconStart?: IconName; iconStart?: IconName;
truncated?: boolean; truncated?: boolean;
......
...@@ -42,7 +42,6 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( ...@@ -42,7 +42,6 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
className="group" className="group"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
cursor={ href ? 'pointer' : 'default' }
{ ...rest } { ...rest }
> >
{ children } { children }
...@@ -54,7 +53,7 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( ...@@ -54,7 +53,7 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
return ( return (
<Skeleton loading={ loading } asChild> <Skeleton loading={ loading } asChild>
<ChakraLink asChild ref={ ref } cursor={ href ? 'pointer' : 'default' } { ...rest }> <ChakraLink asChild ref={ ref } { ...rest }>
{ href ? <NextLink href={ href as NextLinkProps['href'] } scroll={ scroll }>{ children }</NextLink> : <span>{ children }</span> } { href ? <NextLink href={ href as NextLinkProps['href'] } scroll={ scroll }>{ children }</NextLink> : <span>{ children }</span> }
</ChakraLink> </ChakraLink>
</Skeleton> </Skeleton>
......
...@@ -62,7 +62,7 @@ const TransactionPageContent = () => { ...@@ -62,7 +62,7 @@ const TransactionPageContent = () => {
// config.features.userOps.isEnabled ? // config.features.userOps.isEnabled ?
// { id: 'user_ops', title: 'User operations', component: <TxUserOps txQuery={ txQuery }/> } : // { id: 'user_ops', title: 'User operations', component: <TxUserOps txQuery={ txQuery }/> } :
// undefined, // undefined,
// { id: 'internal', title: 'Internal txns', component: <TxInternals txQuery={ txQuery }/> }, { id: 'internal', title: 'Internal txns', component: <TxInternals txQuery={ txQuery }/> },
// config.features.dataAvailability.isEnabled && txQuery.data?.blob_versioned_hashes?.length ? // config.features.dataAvailability.isEnabled && txQuery.data?.blob_versioned_hashes?.length ?
// { id: 'blobs', title: 'Blobs', component: <TxBlobs txQuery={ txQuery }/> } : // { id: 'blobs', title: 'Blobs', component: <TxBlobs txQuery={ txQuery }/> } :
// undefined, // undefined,
......
...@@ -76,7 +76,13 @@ const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => { ...@@ -76,7 +76,13 @@ const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => {
return ( return (
<EntityTagTooltip data={ data }> <EntityTagTooltip data={ data }>
<Link external={ linkParams?.type === 'external' } href={ linkParams?.href } onClick={ handleLinkClick } noIcon> <Link
external={ linkParams?.type === 'external' }
href={ linkParams?.href }
onClick={ handleLinkClick }
noIcon
cursor={ hasLink ? 'pointer' : 'default' }
>
<Tag <Tag
bg={ data.meta?.bgColor } bg={ data.meta?.bgColor }
color={ data.meta?.textColor } color={ data.meta?.textColor }
......
...@@ -5,6 +5,7 @@ import { Alert } from 'toolkit/chakra/alert'; ...@@ -5,6 +5,7 @@ import { Alert } from 'toolkit/chakra/alert';
import { Link } from 'toolkit/chakra/link'; import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table'; import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TxPendingAlert from 'ui/tx/TxPendingAlert';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
...@@ -35,7 +36,7 @@ const AlertsShowcase = () => { ...@@ -35,7 +36,7 @@ const AlertsShowcase = () => {
<SectionHeader>Variant</SectionHeader> <SectionHeader>Variant</SectionHeader>
<SamplesStack> <SamplesStack>
<Sample label="variant: subtle"> <Sample label="variant: subtle">
<Alert status="info" title="Info"> Alert content </Alert> <Alert status="info" title="Info" showIcon> Alert content </Alert>
</Sample> </Sample>
</SamplesStack> </SamplesStack>
</Section> </Section>
...@@ -102,7 +103,7 @@ const AlertsShowcase = () => { ...@@ -102,7 +103,7 @@ const AlertsShowcase = () => {
<SectionSubHeader>Multiple lines</SectionSubHeader> <SectionSubHeader>Multiple lines</SectionSubHeader>
<SamplesStack> <SamplesStack>
<Sample label="multiple lines, with title, inline=false"> <Sample label="multiple lines, with title, inline=false">
<Alert status="warning" title="Warning" inline={ false } maxWidth="500px"> <Alert status="warning" title="Warning" inline={ false } maxWidth="500px" showIcon>
<Box> <Box>
Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon! Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!
</Box> </Box>
...@@ -115,6 +116,9 @@ const AlertsShowcase = () => { ...@@ -115,6 +116,9 @@ const AlertsShowcase = () => {
</Box> </Box>
</Alert> </Alert>
</Sample> </Sample>
<Sample label="with spinner">
<TxPendingAlert/>
</Sample>
</SamplesStack> </SamplesStack>
</Section> </Section>
</Container> </Container>
......
import { Show, Hide } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
...@@ -22,14 +22,14 @@ import TxSocketAlert from 'ui/tx/TxSocketAlert'; ...@@ -22,14 +22,14 @@ import TxSocketAlert from 'ui/tx/TxSocketAlert';
import type { TxQuery } from './useTxQuery'; import type { TxQuery } from './useTxQuery';
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = { const SORT_SEQUENCE: Record<SortField, Array<Sort>> = {
value: [ 'value-desc', 'value-asc', undefined ], value: [ 'value-desc', 'value-asc', 'default' ],
'gas-limit': [ 'gas-limit-desc', 'gas-limit-asc', undefined ], 'gas-limit': [ 'gas-limit-desc', 'gas-limit-asc', 'default' ],
}; };
const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE); const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
const sortFn = (sort: Sort | undefined) => (a: InternalTransaction, b: InternalTransaction) => { const sortFn = (sort: Sort) => (a: InternalTransaction, b: InternalTransaction) => {
switch (sort) { switch (sort) {
case 'value-desc': { case 'value-desc': {
return compareBns(b.value, a.value); return compareBns(b.value, a.value);
...@@ -68,7 +68,7 @@ const TxInternals = ({ txQuery }: Props) => { ...@@ -68,7 +68,7 @@ const TxInternals = ({ txQuery }: Props) => {
// filters are not implemented yet in api // filters are not implemented yet in api
// const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]); // const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]);
// const [ searchTerm, setSearchTerm ] = React.useState<string>(''); // const [ searchTerm, setSearchTerm ] = React.useState<string>('');
const [ sort, setSort ] = React.useState<Sort>(); const [ sort, setSort ] = React.useState<Sort>('default');
const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'tx_internal_txs', resourceName: 'tx_internal_txs',
pathParams: { hash: txQuery.data?.hash }, pathParams: { hash: txQuery.data?.hash },
...@@ -103,8 +103,8 @@ const TxInternals = ({ txQuery }: Props) => { ...@@ -103,8 +103,8 @@ const TxInternals = ({ txQuery }: Props) => {
const content = filteredData ? ( const content = filteredData ? (
<> <>
<Show below="lg" ssr={ false }><TxInternalsList data={ filteredData } isLoading={ isPlaceholderData }/></Show> <Box hideFrom="lg"><TxInternalsList data={ filteredData } isLoading={ isPlaceholderData }/></Box>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
<TxInternalsTable <TxInternalsTable
data={ filteredData } data={ filteredData }
sort={ sort } sort={ sort }
...@@ -112,7 +112,7 @@ const TxInternals = ({ txQuery }: Props) => { ...@@ -112,7 +112,7 @@ const TxInternals = ({ txQuery }: Props) => {
top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 } top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</Hide> </Box>
</> </>
) : null; ) : null;
...@@ -127,15 +127,16 @@ const TxInternals = ({ txQuery }: Props) => { ...@@ -127,15 +127,16 @@ const TxInternals = ({ txQuery }: Props) => {
return ( return (
<DataListDisplay <DataListDisplay
isError={ isError || txQuery.isError } isError={ isError || txQuery.isError }
items={ data?.items } itemsNum={ data?.items.length }
emptyText="There are no internal transactions for this transaction." emptyText="There are no internal transactions for this transaction."
// filterProps={{ // filterProps={{
// emptyFilteredText: `Couldn${ apos }t find any transaction that matches your query.`. // emptyFilteredText: `Couldn${ apos }t find any transaction that matches your query.`.
// hasActiveFilters: Boolean(filters.length || searchTerm), // hasActiveFilters: Boolean(filters.length || searchTerm),
// }} // }}
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
import { Alert, Spinner } from '@chakra-ui/react'; import { Spinner } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
const TxPendingAlert = () => { const TxPendingAlert = () => {
return ( return (
<Alert> <Alert startElement={ <Spinner size="sm" my={ 1 }/> }>
<Spinner size="sm" mr={ 2 }/>
This transaction is pending confirmation. This transaction is pending confirmation.
</Alert> </Alert>
); );
......
...@@ -6,9 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -6,9 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Badge } from 'toolkit/chakra/badge';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressFromTo from 'ui/shared/address/AddressFromTo'; import AddressFromTo from 'ui/shared/address/AddressFromTo';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TxStatus from 'ui/shared/statusTag/TxStatus'; import TxStatus from 'ui/shared/statusTag/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
...@@ -22,7 +22,7 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -22,7 +22,7 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit:
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
{ typeTitle && <Tag colorScheme="cyan" isLoading={ isLoading }>{ typeTitle }</Tag> } { typeTitle && <Badge colorPalette="cyan" loading={ isLoading }>{ typeTitle }</Badge> }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/> <TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
</Flex> </Flex>
<AddressFromTo <AddressFromTo
...@@ -32,15 +32,15 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -32,15 +32,15 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit:
w="100%" w="100%"
fontWeight="500" fontWeight="500"
/> />
<HStack spacing={ 3 }> <HStack gap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Value { currencyUnits.ether }</Skeleton> <Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Value { currencyUnits.ether }</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"> <Skeleton loading={ isLoading } fontSize="sm" color="text_secondary">
{ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } { BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton> </Skeleton>
</HStack> </HStack>
<HStack spacing={ 3 }> <HStack gap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Gas limit</Skeleton> <Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Gas limit</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary">{ BigNumber(gasLimit).toFormat() }</Skeleton> <Skeleton loading={ isLoading } fontSize="sm" color="text_secondary">{ BigNumber(gasLimit).toFormat() }</Skeleton>
</HStack> </HStack>
</ListItemMobile> </ListItemMobile>
); );
......
import { Table, Tbody, Tr, Th, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { default as Thead } from 'ui/shared/TheadSticky';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem'; import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
import type { Sort, SortField } from 'ui/tx/internals/utils'; import type { Sort, SortField } from 'ui/tx/internals/utils';
...@@ -23,31 +23,31 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) = ...@@ -23,31 +23,31 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
return ( return (
<AddressHighlightProvider> <AddressHighlightProvider>
<Table> <TableRoot>
<Thead top={ top }> <TableHeaderSticky top={ top }>
<Tr> <TableRow>
<Th width="28%">Type</Th> <TableColumnHeader width="28%">Type</TableColumnHeader>
<Th width="40%">From/To</Th> <TableColumnHeader width="40%">From/To</TableColumnHeader>
<Th width="16%" isNumeric> <TableColumnHeader width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }>
{ sort?.includes('value') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('value') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Value { currencyUnits.ether } Value { currencyUnits.ether }
</Link> </Link>
</Th> </TableColumnHeader>
<Th width="16%" isNumeric> <TableColumnHeader width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }>
{ sort?.includes('gas-limit') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('gas-limit') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Gas limit { currencyUnits.ether } Gas limit { currencyUnits.ether }
</Link> </Link>
</Th> </TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ data.map((item, index) => ( { data.map((item, index) => (
<TxInternalsTableItem key={ item.index.toString() + (isLoading ? index : '') } { ...item } isLoading={ isLoading }/> <TxInternalsTableItem key={ item.index.toString() + (isLoading ? index : '') } { ...item } isLoading={ isLoading }/>
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
</AddressHighlightProvider> </AddressHighlightProvider>
); );
}; };
......
import { Tr, Td, Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import { Badge } from 'toolkit/chakra/badge';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressFromTo from 'ui/shared/address/AddressFromTo'; import AddressFromTo from 'ui/shared/address/AddressFromTo';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import TxStatus from 'ui/shared/statusTag/TxStatus'; import TxStatus from 'ui/shared/statusTag/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
...@@ -20,35 +21,35 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -20,35 +21,35 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit:
const toData = to ? to : createdContract; const toData = to ? to : createdContract;
return ( return (
<Tr alignItems="top"> <TableRow alignItems="top">
<Td> <TableCell>
<Flex rowGap={ 2 } flexWrap="wrap"> <Flex rowGap={ 2 } flexWrap="wrap">
{ typeTitle && ( { typeTitle && (
<Box w="126px" display="inline-block"> <Box w="126px" display="inline-block">
<Tag colorScheme="cyan" mr={ 5 } isLoading={ isLoading }>{ typeTitle }</Tag> <Badge colorPalette="cyan" mr={ 5 } loading={ isLoading }>{ typeTitle }</Badge>
</Box> </Box>
) } ) }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/> <TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
</Flex> </Flex>
</Td> </TableCell>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
<AddressFromTo <AddressFromTo
from={ from } from={ from }
to={ toData } to={ toData }
isLoading={ isLoading } isLoading={ isLoading }
/> />
</Td> </TableCell>
<Td isNumeric verticalAlign="middle"> <TableCell isNumeric verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } { BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td isNumeric verticalAlign="middle"> <TableCell isNumeric verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ BigNumber(gasLimit).toFormat() } { BigNumber(gasLimit).toFormat() }
</Skeleton> </Skeleton>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
import type { TxInternalsType } from 'types/api/internalTransaction'; import type { TxInternalsType } from 'types/api/internalTransaction';
export type Sort = 'value-asc' | 'value-desc' | 'gas-limit-asc' | 'gas-limit-desc'; export type Sort = 'value-asc' | 'value-desc' | 'gas-limit-asc' | 'gas-limit-desc' | 'default';
export type SortField = 'value' | 'gas-limit'; export type SortField = 'value' | 'gas-limit';
interface TxInternalsTypeItem { interface TxInternalsTypeItem {
......
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