Commit 3cfd4d1a authored by tom's avatar tom

block tx tab

parent 3bff0002
...@@ -27,6 +27,7 @@ const RESTRICTED_MODULES = { ...@@ -27,6 +27,7 @@ const RESTRICTED_MODULES = {
{ name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' }, { name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' },
{ name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' }, { name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' },
{ name: 'ui/shared/Tabs/RoutedTabs', message: 'Please use RoutedTabs component from toolkit/components/RoutedTabs instead' }, { name: 'ui/shared/Tabs/RoutedTabs', message: 'Please use RoutedTabs component from toolkit/components/RoutedTabs instead' },
{ name: 'ui/shared/chakra/Tag', message: 'Please use Tag component from toolkit/chakra instead' },
{ {
name: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ importNames: [
......
import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react'; import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
import { Badge as ChakraBadge } from '@chakra-ui/react'; import { chakra, Badge as ChakraBadge } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
export interface BadgeProps extends ChakraBadgeProps { export interface BadgeProps extends ChakraBadgeProps {
loading?: boolean; loading?: boolean;
iconStart?: IconName; iconStart?: IconName;
truncated?: boolean;
} }
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>( export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
function Badge(props, ref) { function Badge(props, ref) {
const { loading, iconStart, children, asChild = true, ...rest } = props; const { loading, iconStart, children, asChild = true, truncated = false, ...rest } = props;
const child = <chakra.span overflow="hidden" textOverflow="ellipsis">{ children }</chakra.span>;
const childrenElement = truncated ? (
<TruncatedTextTooltip label={ children }>
{ child }
</TruncatedTextTooltip>
) : child;
return ( return (
<Skeleton loading={ loading } asChild={ asChild }> <Skeleton loading={ loading } asChild={ asChild }>
<ChakraBadge ref={ ref } display="flex" alignItems="center" gap={ 1 } { ...rest }> <ChakraBadge ref={ ref } display="inline-flex" alignItems="center" whiteSpace="nowrap" { ...rest }>
{ iconStart && <IconSvg name={ iconStart } boxSize="10px"/> } { iconStart && <IconSvg name={ iconStart } boxSize="10px"/> }
{ children } { childrenElement }
</ChakraBadge> </ChakraBadge>
</Skeleton> </Skeleton>
); );
......
...@@ -6,6 +6,7 @@ import { Skeleton } from './skeleton'; ...@@ -6,6 +6,7 @@ import { Skeleton } from './skeleton';
export interface IconButtonProps extends ButtonProps {} export interface IconButtonProps extends ButtonProps {}
// TODO @tom2drum variants for icon buttons: prev-next, top-bar, copy-to-clipboard // TODO @tom2drum variants for icon buttons: prev-next, top-bar, copy-to-clipboard
// TODO @tom2drum fix loading state for outlined variant
export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>( export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
function IconButton(props, ref) { function IconButton(props, ref) {
......
...@@ -15,6 +15,7 @@ const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabI ...@@ -15,6 +15,7 @@ const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabI
fontWeight={ 600 } fontWeight={ 600 }
mx={ size === 'sm' ? 3 : 4 } mx={ size === 'sm' ? 3 : 4 }
flexShrink={ 0 } flexShrink={ 0 }
loading
> >
{ typeof title === 'string' ? title : title() } { typeof title === 'string' ? title : title() }
</Skeleton> </Skeleton>
......
...@@ -333,6 +333,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -333,6 +333,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
backTo: { value: '{colors.gray.400}' }, backTo: { value: '{colors.gray.400}' },
externalLink: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.400}' } }, externalLink: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.400}' } },
}, },
address: {
highlighted: {
bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.900}' } },
border: { value: { _light: '{colors.blue.200}', _dark: '{colors.blue.600}' } },
},
},
global: { global: {
body: { body: {
bg: { value: { _light: '{colors.white}', _dark: '{colors.black}' } }, bg: { value: { _light: '{colors.white}', _dark: '{colors.black}' } },
......
import type { SystemStyleObject } from '@chakra-ui/theme-tools'; import type { SystemConfig } from '@chakra-ui/react';
// TODO @tom2drum check address highlight feature
import addressEntity from './globals/address-entity'; import addressEntity from './globals/address-entity';
import recaptcha from './globals/recaptcha'; import recaptcha from './globals/recaptcha';
import scrollbar from './globals/scrollbar'; import scrollbar from './globals/scrollbar';
const globalCss: Record<string, SystemStyleObject> = { const globalCss: SystemConfig['globalCss'] = {
body: { body: {
bg: 'global.body.bg', bg: 'global.body.bg',
color: 'global.body.fg', color: 'global.body.fg',
'-webkit-tap-highlight-color': 'transparent', WebkitTapHighlightColor: 'transparent',
fontVariantLigatures: 'no-contextual', fontVariantLigatures: 'no-contextual',
}, },
mark: { mark: {
...@@ -25,7 +24,7 @@ const globalCss: Record<string, SystemStyleObject> = { ...@@ -25,7 +24,7 @@ const globalCss: Record<string, SystemStyleObject> = {
}, },
...recaptcha, ...recaptcha,
...scrollbar, ...scrollbar,
// ...addressEntity(props), ...addressEntity,
}; };
export default globalCss; export default globalCss;
import { mode } from '@chakra-ui/theme-tools'; const styles = {
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const styles = (props: StyleFunctionProps) => {
return {
'.address-entity': { '.address-entity': {
'&.address-entity_highlighted': { '&.address-entity_highlighted': {
_before: { _before: {
...@@ -13,13 +9,13 @@ const styles = (props: StyleFunctionProps) => { ...@@ -13,13 +9,13 @@ const styles = (props: StyleFunctionProps) => {
pr: 0, pr: 0,
top: '-5px', top: '-5px',
left: '-5px', left: '-5px',
width: `100%`, width: `calc(100% + 6px)`,
height: '100%', height: 'calc(100% + 10px)',
borderRadius: 'base', borderRadius: 'base',
borderColor: mode('blue.200', 'blue.600')(props), borderColor: 'address.highlighted.border',
borderWidth: '1px', borderWidth: '1px',
borderStyle: 'dashed', borderStyle: 'dashed',
bgColor: mode('blue.50', 'blue.900')(props), bgColor: 'address.highlighted.bg',
zIndex: -1, zIndex: -1,
}, },
}, },
...@@ -31,7 +27,6 @@ const styles = (props: StyleFunctionProps) => { ...@@ -31,7 +27,6 @@ const styles = (props: StyleFunctionProps) => {
}, },
}, },
}, },
};
}; };
export default styles; export default styles;
...@@ -7,6 +7,8 @@ export const recipe = defineRecipe({ ...@@ -7,6 +7,8 @@ export const recipe = defineRecipe({
borderRadius: 'sm', borderRadius: 'sm',
gap: '1', gap: '1',
fontWeight: '500', fontWeight: '500',
width: 'fit-content',
maxWidth: '100%',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
userSelect: 'none', userSelect: 'none',
_loading: { _loading: {
......
...@@ -87,7 +87,7 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => { ...@@ -87,7 +87,7 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => {
const content = query.data?.items ? ( const content = query.data?.items ? (
<> <>
<Box display={{ base: 'block', lg: 'none' }}> <Box hideFrom="lg">
{ query.pagination.page === 1 && enableSocket && ( { query.pagination.page === 1 && enableSocket && (
<SocketNewItemsNotice.Mobile <SocketNewItemsNotice.Mobile
url={ window.location.href } url={ window.location.href }
...@@ -99,7 +99,7 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => { ...@@ -99,7 +99,7 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => {
) } ) }
<BlocksList data={ query.data.items } isLoading={ query.isPlaceholderData } page={ query.pagination.page }/> <BlocksList data={ query.data.items } isLoading={ query.isPlaceholderData } page={ query.pagination.page }/>
</Box> </Box>
<Box display={{ base: 'none', lg: 'block' }}> <Box hideBelow="lg">
<BlocksTable <BlocksTable
data={ query.data.items } data={ query.data.items }
top={ top || (query.pagination.isVisible ? TABS_HEIGHT : 0) } top={ top || (query.pagination.isVisible ? TABS_HEIGHT : 0) }
......
...@@ -68,16 +68,16 @@ const BlockPageContent = () => { ...@@ -68,16 +68,16 @@ const BlockPageContent = () => {
</> </>
), ),
}, },
// { {
// id: 'txs', id: 'txs',
// title: 'Transactions', title: 'Transactions',
// component: ( component: (
// <> <>
// { blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> } { blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> }
// <TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/> <TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>
// </> </>
// ), ),
// }, },
// config.features.dataAvailability.isEnabled && blockQuery.data?.blob_transaction_count ? // config.features.dataAvailability.isEnabled && blockQuery.data?.blob_transaction_count ?
// { // {
// id: 'blob_txs', // id: 'blob_txs',
......
...@@ -18,7 +18,7 @@ interface Props { ...@@ -18,7 +18,7 @@ interface Props {
const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className, accuracy, accuracyUsd, isLoading }: Props) => { const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className, accuracy, accuracyUsd, isLoading }: Props) => {
if (isLoading) { if (isLoading) {
return ( return (
<Skeleton className={ className } display="inline-block">0.00 ($0.00)</Skeleton> <Skeleton className={ className } loading display="inline-block">0.00 ($0.00)</Skeleton>
); );
} }
......
...@@ -172,6 +172,7 @@ const AddressEntry = (props: EntityProps) => { ...@@ -172,6 +172,7 @@ const AddressEntry = (props: EntityProps) => {
onMouseLeave={ highlightContext?.onMouseLeave } onMouseLeave={ highlightContext?.onMouseLeave }
position="relative" position="relative"
zIndex={ 0 } zIndex={ 0 }
fontWeight="medium"
> >
<Icon { ...partsProps.icon }/> <Icon { ...partsProps.icon }/>
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> } { props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
......
...@@ -35,7 +35,7 @@ const TxFee = ({ className, tx, accuracy, accuracyUsd, isLoading, withCurrency = ...@@ -35,7 +35,7 @@ const TxFee = ({ className, tx, accuracy, accuracyUsd, isLoading, withCurrency =
<Skeleton whiteSpace="pre-wrap" wordBreak="break-word" loading={ isLoading } display="flex" flexWrap="wrap" className={ className }> <Skeleton whiteSpace="pre-wrap" wordBreak="break-word" loading={ isLoading } display="flex" flexWrap="wrap" className={ className }>
<span>{ valueStr } </span> <span>{ valueStr } </span>
<TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/> <TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/>
{ usd && withUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> } { usd && withUsd && <chakra.span color="text.secondary"> (${ usd })</chakra.span> }
</Skeleton> </Skeleton>
); );
} }
......
import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Badge } from 'toolkit/chakra/badge'; import { Badge } from 'toolkit/chakra/badge';
...@@ -53,6 +54,8 @@ const BadgesShowcase = () => { ...@@ -53,6 +54,8 @@ const BadgesShowcase = () => {
<Badge colorPalette="purple" loading>Content</Badge> <Badge colorPalette="purple" loading>Content</Badge>
</Sample> </Sample>
</SamplesStack> </SamplesStack>
</Section>
<Section>
<SectionHeader>Size</SectionHeader> <SectionHeader>Size</SectionHeader>
<SamplesStack> <SamplesStack>
<Sample label="size: md"> <Sample label="size: md">
...@@ -68,6 +71,18 @@ const BadgesShowcase = () => { ...@@ -68,6 +71,18 @@ const BadgesShowcase = () => {
</Sample> </Sample>
</SamplesStack> </SamplesStack>
</Section> </Section>
<Section>
<SectionHeader>Truncate</SectionHeader>
<SamplesStack>
<Sample label="truncated: true">
<Box maxW="150px">
<Badge truncated>
Very long content that should be truncated
</Badge>
</Box>
</Sample>
</SamplesStack>
</Section>
<Section> <Section>
<SectionHeader>Icon</SectionHeader> <SectionHeader>Icon</SectionHeader>
<SamplesStack> <SamplesStack>
......
...@@ -3,6 +3,7 @@ import React from 'react'; ...@@ -3,6 +3,7 @@ import React from 'react';
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs';
import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs';
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
...@@ -61,6 +62,13 @@ const TabsShowcase = () => { ...@@ -61,6 +62,13 @@ const TabsShowcase = () => {
/> />
</Sample> </Sample>
</SamplesStack> </SamplesStack>
<SectionSubHeader>Tabs skeleton</SectionSubHeader>
<SamplesStack>
<Sample>
<RoutedTabsSkeleton tabs={ tabs }/>
</Sample>
</SamplesStack>
</Section> </Section>
</Container> </Container>
); );
......
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
import type { TransactionType } from 'types/api/transaction'; import type { TransactionType } from 'types/api/transaction';
import Tag from 'ui/shared/chakra/Tag'; import { Badge } from 'toolkit/chakra/badge';
import { camelCaseToSentence } from './noves/utils'; import { camelCaseToSentence } from './noves/utils';
import TxType from './TxType'; import TxType from './TxType';
...@@ -22,9 +22,9 @@ const TxTranslationType = ({ types, isLoading, translatationType }: Props) => { ...@@ -22,9 +22,9 @@ const TxTranslationType = ({ types, isLoading, translatationType }: Props) => {
} }
return ( return (
<Tag colorScheme="purple" isLoading={ isLoading }> <Badge colorScheme="purple" loading={ isLoading }>
{ camelCaseToSentence(translatationType) } { camelCaseToSentence(translatationType) }
</Tag> </Badge>
); );
}; };
......
import { Show, Hide } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
...@@ -69,7 +69,7 @@ const TxsContent = ({ ...@@ -69,7 +69,7 @@ const TxsContent = ({
const content = itemsWithTranslation ? ( const content = itemsWithTranslation ? (
<> <>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
<TxsList <TxsList
showBlockInfo={ showBlockInfo } showBlockInfo={ showBlockInfo }
showSocketInfo={ showSocketInfo } showSocketInfo={ showSocketInfo }
...@@ -80,8 +80,8 @@ const TxsContent = ({ ...@@ -80,8 +80,8 @@ const TxsContent = ({
currentAddress={ currentAddress } currentAddress={ currentAddress }
items={ itemsWithTranslation } items={ itemsWithTranslation }
/> />
</Show> </Box>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
<TxsTable <TxsTable
txs={ itemsWithTranslation } txs={ itemsWithTranslation }
sort={ onSortToggle } sort={ onSortToggle }
...@@ -95,7 +95,7 @@ const TxsContent = ({ ...@@ -95,7 +95,7 @@ const TxsContent = ({
enableTimeIncrement={ enableTimeIncrement } enableTimeIncrement={ enableTimeIncrement }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</Hide> </Box>
</> </>
) : null; ) : null;
...@@ -121,11 +121,12 @@ const TxsContent = ({ ...@@ -121,11 +121,12 @@ const TxsContent = ({
return ( return (
<DataListDisplay <DataListDisplay
isError={ isError } isError={ isError }
items={ itemsWithTranslation } itemsNum={ itemsWithTranslation?.length }
emptyText="There are no transactions." emptyText="There are no transactions."
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
...@@ -29,13 +29,14 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps ...@@ -29,13 +29,14 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps
<ActionBar className={ className }> <ActionBar className={ className }>
<HStack> <HStack>
{ filterComponent } { filterComponent }
<Sort { /* TODO @tom2drum fix sort select */ }
{ /* <Sort
name="transactions_sorting" name="transactions_sorting"
defaultValue={ sorting } defaultValue={ sorting }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
onChange={ setSorting } onChange={ setSorting }
isLoading={ paginationProps.isLoading } isLoading={ paginationProps.isLoading }
/> /> */ }
{ /* api is not implemented */ } { /* api is not implemented */ }
{ /* <FilterInput { /* <FilterInput
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
......
...@@ -3,6 +3,7 @@ import React from 'react'; ...@@ -3,6 +3,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import useInitialList from 'lib/hooks/useInitialList';
import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; import useLazyRenderedList from 'lib/hooks/useLazyRenderedList';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
...@@ -21,6 +22,11 @@ interface Props { ...@@ -21,6 +22,11 @@ interface Props {
const TxsList = (props: Props) => { const TxsList = (props: Props) => {
const { cutRef, renderedItemsNum } = useLazyRenderedList(props.items, !props.isLoading); const { cutRef, renderedItemsNum } = useLazyRenderedList(props.items, !props.isLoading);
const initialList = useInitialList({
data: props.items ?? [],
idFn: (item) => item.hash,
enabled: !props.isLoading,
});
return ( return (
<Box> <Box>
...@@ -40,6 +46,7 @@ const TxsList = (props: Props) => { ...@@ -40,6 +46,7 @@ const TxsList = (props: Props) => {
currentAddress={ props.currentAddress } currentAddress={ props.currentAddress }
enableTimeIncrement={ props.enableTimeIncrement } enableTimeIncrement={ props.enableTimeIncrement }
isLoading={ props.isLoading } isLoading={ props.isLoading }
animation={ initialList.getAnimationProp(tx) }
/> />
)) } )) }
<Box ref={ cutRef } h={ 0 }/> <Box ref={ cutRef } h={ 0 }/>
......
...@@ -11,8 +11,8 @@ import config from 'configs/app'; ...@@ -11,8 +11,8 @@ import config from 'configs/app';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
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 BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
...@@ -31,13 +31,14 @@ type Props = { ...@@ -31,13 +31,14 @@ type Props = {
currentAddress?: string; currentAddress?: string;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
isLoading?: boolean; isLoading?: boolean;
animation?: string;
}; };
const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeIncrement }: Props) => { const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeIncrement, animation }: Props) => {
const dataTo = tx.to ? tx.to : tx.created_contract; const dataTo = tx.to ? tx.to : tx.created_contract;
return ( return (
<ListItemMobile display="block" width="100%" isAnimated key={ tx.hash }> <ListItemMobile display="block" width="100%" animation={ animation } key={ tx.hash }>
<Flex justifyContent="space-between" mt={ 4 }> <Flex justifyContent="space-between" mt={ 4 }>
<HStack flexWrap="wrap"> <HStack flexWrap="wrap">
{ tx.translation ? ( { tx.translation ? (
...@@ -75,9 +76,9 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -75,9 +76,9 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Flex> </Flex>
{ tx.method && ( { tx.method && (
<Flex mt={ 3 }> <Flex mt={ 3 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Method </Skeleton> <Skeleton loading={ isLoading } display="inline-block" whiteSpace="pre">Method </Skeleton>
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
color="text_secondary" color="text_secondary"
overflow="hidden" overflow="hidden"
whiteSpace="nowrap" whiteSpace="nowrap"
...@@ -89,7 +90,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -89,7 +90,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
) } ) }
{ showBlockInfo && tx.block_number !== null && ( { showBlockInfo && tx.block_number !== null && (
<Flex mt={ 2 }> <Flex mt={ 2 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Block </Skeleton> <Skeleton loading={ isLoading } display="inline-block" whiteSpace="pre">Block </Skeleton>
<BlockEntity <BlockEntity
isLoading={ isLoading } isLoading={ isLoading }
number={ tx.block_number } number={ tx.block_number }
...@@ -107,11 +108,13 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -107,11 +108,13 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
/> />
{ !config.UI.views.tx.hiddenFields?.value && ( { !config.UI.views.tx.hiddenFields?.value && (
<Flex mt={ 2 } columnGap={ 2 }> <Flex mt={ 2 } columnGap={ 2 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Value</Skeleton> <Skeleton loading={ isLoading } display="inline-block" whiteSpace="pre">Value</Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary" whiteSpace="pre"> <Skeleton loading={ isLoading } display="inline-block" color="text.secondary" whiteSpace="pre">
<span>
{ getValueWithUnit(tx.value).toFormat() } { getValueWithUnit(tx.value).toFormat() }
{ space } { space }
{ currencyUnits.ether } { currencyUnits.ether }
</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
) } ) }
...@@ -119,7 +122,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -119,7 +122,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
<Flex mt={ 2 } mb={ 3 } columnGap={ 2 }> <Flex mt={ 2 } mb={ 3 } columnGap={ 2 }>
{ (tx.stability_fee !== undefined || tx.fee.value !== null) && ( { (tx.stability_fee !== undefined || tx.fee.value !== null) && (
<> <>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Fee</Skeleton> <Skeleton loading={ isLoading } display="inline-block" whiteSpace="pre">Fee</Skeleton>
<TxFee tx={ tx } isLoading={ isLoading }/> <TxFee tx={ tx } isLoading={ isLoading }/>
</> </>
) } ) }
......
import { Link, Table, Tbody, Tr, Th } from '@chakra-ui/react';
import { AnimatePresence } from 'framer-motion';
import React from 'react'; import React from 'react';
import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'types/api/transaction'; import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'types/api/transaction';
import config from 'configs/app'; import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useInitialList from 'lib/hooks/useInitialList';
import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; import useLazyRenderedList from 'lib/hooks/useLazyRenderedList';
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 * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TheadSticky from 'ui/shared/TheadSticky';
import TxsTableItem from './TxsTableItem'; import TxsTableItem from './TxsTableItem';
...@@ -42,6 +42,11 @@ const TxsTable = ({ ...@@ -42,6 +42,11 @@ const TxsTable = ({
isLoading, isLoading,
}: Props) => { }: Props) => {
const { cutRef, renderedItemsNum } = useLazyRenderedList(txs, !isLoading); const { cutRef, renderedItemsNum } = useLazyRenderedList(txs, !isLoading);
const initialList = useInitialList({
data: txs ?? [],
idFn: (item) => item.hash,
enabled: !isLoading,
});
const feeCurrency = config.UI.views.tx.hiddenFields?.fee_currency || config.chain.hasMultipleGasCurrencies ? const feeCurrency = config.UI.views.tx.hiddenFields?.fee_currency || config.chain.hasMultipleGasCurrencies ?
'' : '' :
...@@ -49,44 +54,44 @@ const TxsTable = ({ ...@@ -49,44 +54,44 @@ const TxsTable = ({
return ( return (
<AddressHighlightProvider> <AddressHighlightProvider>
<Table minWidth="950px"> <TableRoot minWidth="950px">
<TheadSticky top={ top }> <TableHeaderSticky top={ top }>
<Tr> <TableRow>
<Th width="54px"></Th> <TableColumnHeader width="54px"></TableColumnHeader>
<Th width="180px">Txn hash</Th> <TableColumnHeader width="180px">Txn hash</TableColumnHeader>
<Th width="160px">Type</Th> <TableColumnHeader width="160px">Type</TableColumnHeader>
<Th width="20%">Method</Th> <TableColumnHeader width="20%">Method</TableColumnHeader>
{ showBlockInfo && ( { showBlockInfo && (
<Th width="18%"> <TableColumnHeader width="18%">
<Link onClick={ isLoading ? undefined : sort('block_number') } display="flex" alignItems="center"> <Link onClick={ isLoading ? undefined : sort('block_number') } display="flex" alignItems="center">
{ sorting === 'block_number-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> } { sorting === 'block_number-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'block_number-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> } { sorting === 'block_number-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
Block Block
</Link> </Link>
</Th> </TableColumnHeader>
) } ) }
<Th width="224px">From/To</Th> <TableColumnHeader width="224px">From/To</TableColumnHeader>
{ !config.UI.views.tx.hiddenFields?.value && ( { !config.UI.views.tx.hiddenFields?.value && (
<Th width="20%" isNumeric> <TableColumnHeader width="20%" isNumeric>
<Link onClick={ isLoading ? undefined : sort('value') } display="flex" alignItems="center" justifyContent="end"> <Link onClick={ isLoading ? undefined : sort('value') } display="flex" alignItems="center" justifyContent="end">
{ sorting === 'value-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> } { sorting === 'value-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'value-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> } { sorting === 'value-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
{ `Value ${ currencyUnits.ether }` } { `Value ${ currencyUnits.ether }` }
</Link> </Link>
</Th> </TableColumnHeader>
) } ) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && ( { !config.UI.views.tx.hiddenFields?.tx_fee && (
<Th width="20%" isNumeric pr={ 5 }> <TableColumnHeader width="20%" isNumeric pr={ 5 }>
<Link onClick={ isLoading ? undefined : sort('fee') } display="flex" alignItems="center" justifyContent="end"> <Link onClick={ isLoading ? undefined : sort('fee') } display="flex" alignItems="center" justifyContent="end">
{ sorting === 'fee-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> } { sorting === 'fee-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'fee-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> } { sorting === 'fee-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
{ `Fee${ feeCurrency }` } { `Fee${ feeCurrency }` }
</Link> </Link>
</Th> </TableColumnHeader>
) } ) }
</Tr> </TableRow>
</TheadSticky> </TableHeaderSticky>
<Tbody> <TableBody>
{ showSocketInfo && ( { showSocketInfo && (
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
...@@ -95,7 +100,6 @@ const TxsTable = ({ ...@@ -95,7 +100,6 @@ const TxsTable = ({
isLoading={ isLoading } isLoading={ isLoading }
/> />
) } ) }
<AnimatePresence initial={ false }>
{ txs.slice(0, renderedItemsNum).map((item, index) => ( { txs.slice(0, renderedItemsNum).map((item, index) => (
<TxsTableItem <TxsTableItem
key={ item.hash + (isLoading ? index : '') } key={ item.hash + (isLoading ? index : '') }
...@@ -104,11 +108,11 @@ const TxsTable = ({ ...@@ -104,11 +108,11 @@ const TxsTable = ({
currentAddress={ currentAddress } currentAddress={ currentAddress }
enableTimeIncrement={ enableTimeIncrement } enableTimeIncrement={ enableTimeIncrement }
isLoading={ isLoading } isLoading={ isLoading }
animation={ initialList.getAnimationProp(item) }
/> />
)) } )) }
</AnimatePresence> </TableBody>
</Tbody> </TableRoot>
</Table>
<div ref={ cutRef }/> <div ref={ cutRef }/>
</AddressHighlightProvider> </AddressHighlightProvider>
); );
......
import { import { VStack } from '@chakra-ui/react';
Tr,
Td,
VStack,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import config from 'configs/app'; import config from 'configs/app';
import { Badge } from 'toolkit/chakra/badge';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressFromTo from 'ui/shared/address/AddressFromTo'; import AddressFromTo from 'ui/shared/address/AddressFromTo';
import Tag from 'ui/shared/chakra/Tag';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
...@@ -29,29 +25,23 @@ type Props = { ...@@ -29,29 +25,23 @@ type Props = {
currentAddress?: string; currentAddress?: string;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
isLoading?: boolean; isLoading?: boolean;
animation?: string;
}; };
const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, isLoading }: Props) => { const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, isLoading, animation }: Props) => {
const dataTo = tx.to ? tx.to : tx.created_contract; const dataTo = tx.to ? tx.to : tx.created_contract;
return ( return (
<Tr <TableRow key={ tx.hash } animation={ animation }>
as={ motion.tr } <TableCell pl={ 4 }>
initial={{ opacity: 0, scale: 0.97 }}
animate={{ opacity: 1, scale: 1 }}
transitionDuration="normal"
transitionTimingFunction="linear"
key={ tx.hash }
>
<Td pl={ 4 }>
<TxAdditionalInfo tx={ tx } isLoading={ isLoading }/> <TxAdditionalInfo tx={ tx } isLoading={ isLoading }/>
</Td> </TableCell>
<Td pr={ 4 }> <TableCell pr={ 4 }>
<VStack alignItems="start" lineHeight="24px"> <VStack alignItems="start" lineHeight="24px">
<TxEntity <TxEntity
hash={ tx.hash } hash={ tx.hash }
isLoading={ isLoading } isLoading={ isLoading }
fontWeight={ 700 } fontWeight="bold"
noIcon noIcon
maxW="100%" maxW="100%"
truncation="constant_long" truncation="constant_long"
...@@ -60,12 +50,11 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -60,12 +50,11 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
timestamp={ tx.timestamp } timestamp={ tx.timestamp }
enableIncrement={ enableTimeIncrement } enableIncrement={ enableTimeIncrement }
isLoading={ isLoading } isLoading={ isLoading }
color="text_secondary" color="text.secondary"
fontWeight="400"
/> />
</VStack> </VStack>
</Td> </TableCell>
<Td> <TableCell>
<VStack alignItems="start"> <VStack alignItems="start">
{ tx.translation ? ( { tx.translation ? (
<TxTranslationType <TxTranslationType
...@@ -79,16 +68,16 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -79,16 +68,16 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/> <TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
<TxWatchListTags tx={ tx } isLoading={ isLoading }/> <TxWatchListTags tx={ tx } isLoading={ isLoading }/>
</VStack> </VStack>
</Td> </TableCell>
<Td whiteSpace="nowrap"> <TableCell whiteSpace="nowrap">
{ tx.method && ( { tx.method && (
<Tag colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' } isLoading={ isLoading } isTruncated> <Badge colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated>
{ tx.method } <span>{ tx.method }</span>
</Tag> </Badge>
) } ) }
</Td> </TableCell>
{ showBlockInfo && ( { showBlockInfo && (
<Td> <TableCell>
{ tx.block_number && ( { tx.block_number && (
<BlockEntity <BlockEntity
isLoading={ isLoading } isLoading={ isLoading }
...@@ -99,9 +88,9 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -99,9 +88,9 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
fontWeight={ 500 } fontWeight={ 500 }
/> />
) } ) }
</Td> </TableCell>
) } ) }
<Td> <TableCell>
<AddressFromTo <AddressFromTo
from={ tx.from } from={ tx.from }
to={ dataTo } to={ dataTo }
...@@ -110,14 +99,14 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -110,14 +99,14 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
mt="2px" mt="2px"
mode="compact" mode="compact"
/> />
</Td> </TableCell>
{ !config.UI.views.tx.hiddenFields?.value && ( { !config.UI.views.tx.hiddenFields?.value && (
<Td isNumeric> <TableCell isNumeric>
<CurrencyValue value={ tx.value } accuracy={ 8 } isLoading={ isLoading }/> <CurrencyValue value={ tx.value } accuracy={ 8 } isLoading={ isLoading }/>
</Td> </TableCell>
) } ) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && ( { !config.UI.views.tx.hiddenFields?.tx_fee && (
<Td isNumeric maxW="220px"> <TableCell isNumeric maxW="220px">
<TxFee <TxFee
tx={ tx } tx={ tx }
accuracy={ 8 } accuracy={ 8 }
...@@ -125,9 +114,9 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -125,9 +114,9 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
withCurrency={ Boolean(tx.celo || tx.stability_fee) } withCurrency={ Boolean(tx.celo || tx.stability_fee) }
justifyContent="end" justifyContent="end"
/> />
</Td> </TableCell>
) } ) }
</Tr> </TableRow>
); );
}; };
......
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