Commit a86ee441 authored by tom's avatar tom

tx details tab

parent a19110b6
......@@ -36,8 +36,9 @@ const RESTRICTED_MODULES = {
'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
'Heading', 'Badge', 'Tabs',
'Heading', 'Badge', 'Tabs', 'Show', 'Hide',
'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger',
],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
},
......
......@@ -5,12 +5,12 @@ import React from 'react';
import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';
// const Transaction = dynamic(() => import('ui/pages/Transaction'), { ssr: false });
const Transaction = dynamic(() => import('ui/pages/Transaction'), { ssr: false });
const Page: NextPage<Props> = (props: Props) => {
return (
<PageNextJs pathname="/tx/[hash]" query={ props.query }>
{ /* <Transaction/> */ }
<Transaction/>
</PageNextJs>
);
};
......
......@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
// const Transactions = dynamic(() => import('ui/pages/Transactions'), { ssr: false });
const Transactions = dynamic(() => import('ui/pages/Transactions'), { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/txs">
{ /* <Transactions/> */ }
<Transactions/>
</PageNextJs>
);
};
......
'use client';
import { AbsoluteCenter, Menu as ChakraMenu, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { LuCheck, LuChevronRight } from 'react-icons/lu';
interface MenuContentProps extends ChakraMenu.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
}
export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(
function MenuContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraMenu.Positioner>
<ChakraMenu.Content ref={ ref } { ...rest }/>
</ChakraMenu.Positioner>
</Portal>
);
},
);
export const MenuArrow = React.forwardRef<
HTMLDivElement,
ChakraMenu.ArrowProps
>(function MenuArrow(props, ref) {
return (
<ChakraMenu.Arrow ref={ ref } { ...props }>
<ChakraMenu.ArrowTip/>
</ChakraMenu.Arrow>
);
});
export const MenuCheckboxItem = React.forwardRef<
HTMLDivElement,
ChakraMenu.CheckboxItemProps
>(function MenuCheckboxItem(props, ref) {
return (
<ChakraMenu.CheckboxItem ps="8" ref={ ref } { ...props }>
<AbsoluteCenter axis="horizontal" insetStart="4" asChild>
<ChakraMenu.ItemIndicator>
<LuCheck/>
</ChakraMenu.ItemIndicator>
</AbsoluteCenter>
{ props.children }
</ChakraMenu.CheckboxItem>
);
});
export const MenuRadioItem = React.forwardRef<
HTMLDivElement,
ChakraMenu.RadioItemProps
>(function MenuRadioItem(props, ref) {
const { children, ...rest } = props;
return (
<ChakraMenu.RadioItem ps="8" ref={ ref } { ...rest }>
<AbsoluteCenter axis="horizontal" insetStart="4" asChild>
<ChakraMenu.ItemIndicator>
<LuCheck/>
</ChakraMenu.ItemIndicator>
</AbsoluteCenter>
<ChakraMenu.ItemText>{ children }</ChakraMenu.ItemText>
</ChakraMenu.RadioItem>
);
});
export const MenuItemGroup = React.forwardRef<
HTMLDivElement,
ChakraMenu.ItemGroupProps
>(function MenuItemGroup(props, ref) {
const { title, children, ...rest } = props;
return (
<ChakraMenu.ItemGroup ref={ ref } { ...rest }>
{ title && (
<ChakraMenu.ItemGroupLabel userSelect="none">
{ title }
</ChakraMenu.ItemGroupLabel>
) }
{ children }
</ChakraMenu.ItemGroup>
);
});
export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
startIcon?: React.ReactNode;
}
export const MenuTriggerItem = React.forwardRef<
HTMLDivElement,
MenuTriggerItemProps
>(function MenuTriggerItem(props, ref) {
const { startIcon, children, ...rest } = props;
return (
<ChakraMenu.TriggerItem ref={ ref } { ...rest }>
{ startIcon }
{ children }
<LuChevronRight/>
</ChakraMenu.TriggerItem>
);
});
export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup;
export const MenuContextTrigger = ChakraMenu.ContextTrigger;
export const MenuRoot = (props: ChakraMenu.RootProps) => {
const { lazyMount = true, unmountOnExit = true, ...rest } = props;
const positioning = {
placement: 'bottom-start' as const,
...props.positioning,
offset: {
mainAxis: 4,
...props.positioning?.offset,
},
};
return <ChakraMenu.Root { ...rest } positioning={ positioning } lazyMount={ lazyMount } unmountOnExit={ unmountOnExit }/>;
};
export const MenuSeparator = ChakraMenu.Separator;
export const MenuItem = ChakraMenu.Item;
export const MenuItemText = ChakraMenu.ItemText;
export const MenuItemCommand = ChakraMenu.ItemCommand;
export const MenuTrigger = ChakraMenu.Trigger;
......@@ -44,9 +44,13 @@ export const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(
},
);
export const Skeleton = React.forwardRef<HTMLDivElement, ChakraSkeletonProps>(
export interface SkeletonProps extends Omit<ChakraSkeletonProps, 'loading'> {
loading: boolean | undefined;
}
export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
function Skeleton(props, ref) {
const { loading = true, ...rest } = props;
const { loading = false, ...rest } = props;
return <ChakraSkeleton loading={ loading } { ...(loading ? { 'data-loading': true } : {}) } { ...rest } ref={ ref }/>;
},
);
import React from 'react';
import { scroller, Element } from 'react-scroll';
import useUpdateEffect from 'lib/hooks/useUpdateEffect';
import type { LinkProps } from 'toolkit/chakra/link';
import { Link } from 'toolkit/chakra/link';
......@@ -8,14 +9,15 @@ interface Props extends LinkProps {
children: React.ReactNode;
id?: string;
onClick?: () => void;
isExpanded?: boolean;
}
const ID = 'CutLink';
const CutLink = (props: Props) => {
const { children, id = ID, onClick, ...rest } = props;
const { children, id = ID, onClick, isExpanded: isExpandedProp = false, ...rest } = props;
const [ isExpanded, setIsExpanded ] = React.useState(false);
const [ isExpanded, setIsExpanded ] = React.useState(isExpandedProp);
const handleClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
......@@ -26,6 +28,14 @@ const CutLink = (props: Props) => {
onClick?.();
}, [ id, onClick ]);
useUpdateEffect(() => {
setIsExpanded(isExpandedProp);
isExpandedProp && scroller.scrollTo(id, {
duration: 500,
smooth: true,
});
}, [ isExpandedProp, id ]);
const text = isExpanded ? 'Hide details' : 'View details';
return (
......@@ -34,6 +44,7 @@ const CutLink = (props: Props) => {
textStyle="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
w="fit-content"
onClick={ handleClick }
asChild
{ ...rest }
......
......@@ -260,6 +260,13 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
},
},
},
menu: {
item: {
bg: {
highlighted: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
},
},
},
spinner: {
track: {
DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
......
......@@ -8,6 +8,7 @@ import { recipe as drawer } from './drawer.recipe';
import { recipe as field } from './field.recipe';
import { recipe as input } from './input.recipe';
import { recipe as link } from './link.recipe';
import { recipe as menu } from './menu.recipe';
import { recipe as nativeSelect } from './native-select.recipe';
import { recipe as pinInput } from './pin-input.recipe';
import { recipe as popover } from './popover.recipe';
......@@ -40,6 +41,7 @@ export const slotRecipes = {
dialog,
drawer,
field,
menu,
nativeSelect,
pinInput,
popover,
......
import { defineSlotRecipe } from '@chakra-ui/react';
export const recipe = defineSlotRecipe({
slots: [ 'content', 'item', 'itemText', 'itemGroupLabel', 'indicator', 'itemCommand', 'separator' ],
base: {
content: {
outline: 0,
bg: 'popover,bg',
boxShadow: 'popover',
color: 'initial',
maxHeight: 'var(--available-height)',
'--menu-z-index': 'zIndex.dropdown',
zIndex: 'calc(var(--menu-z-index) + var(--layer-index, 0))',
borderRadius: 'md',
overflow: 'hidden',
overflowY: 'auto',
_open: {
animationStyle: 'slide-fade-in',
animationDuration: 'fast',
},
_closed: {
animationStyle: 'slide-fade-out',
animationDuration: 'faster',
},
},
item: {
textDecoration: 'none',
color: 'initial',
userSelect: 'none',
borderRadius: 'none',
width: '100%',
display: 'flex',
cursor: 'pointer',
alignItems: 'center',
textAlign: 'start',
position: 'relative',
flex: '0 0 auto',
outline: 0,
_disabled: {
layerStyle: 'disabled',
},
},
itemText: {
flex: '1',
},
itemGroupLabel: {
px: '2',
py: '1.5',
fontWeight: 'semibold',
textStyle: 'sm',
},
indicator: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: '0',
},
itemCommand: {
opacity: '0.6',
textStyle: 'xs',
ms: 'auto',
ps: '4',
letterSpacing: 'widest',
},
separator: {
height: '1px',
bg: 'bg.muted',
my: '1',
mx: '-1',
},
},
variants: {
variant: {
subtle: {
item: {
_highlighted: {
bg: 'menu.item.bg.highlighted',
},
},
},
},
size: {
md: {
content: {
minW: '150px',
py: '2',
px: '0',
},
item: {
gap: '2',
textStyle: 'md',
py: '2',
px: '4',
},
},
},
},
defaultVariants: {
size: 'md',
variant: 'subtle',
},
});
......@@ -16,10 +16,9 @@ import { route } from 'nextjs-routes';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import { Link } from 'toolkit/chakra/link';
import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress';
import LinkInternal from 'ui/shared/links/LinkInternal';
import AdminSupportText from 'ui/shared/texts/AdminSupportText';
type Fields = RootFields & AddressVerificationFormFirstStepFields;
interface Props {
......@@ -85,7 +84,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
return (
<Box>
<span>The contract source code you entered is not yet verified. Please follow these steps to </span>
<LinkInternal href={ href }>verify the contract</LinkInternal>
<Link href={ href }>verify the contract</Link>
<span>.</span>
</Box>
);
......
......@@ -25,6 +25,7 @@ import AlertsShowcase from 'ui/showcases/Alerts';
import BadgesShowcase from 'ui/showcases/Badges';
import ButtonShowcase from 'ui/showcases/Button';
import LinksShowcase from 'ui/showcases/Links';
import MenusShowcase from 'ui/showcases/Menu';
import PaginationShowcase from 'ui/showcases/Pagination';
import SelectsShowcase from 'ui/showcases/Select';
import TabsShowcase from 'ui/showcases/Tabs';
......@@ -50,6 +51,7 @@ const ChakraShowcases = () => {
<TabsTrigger value="badges">Badges</TabsTrigger>
<TabsTrigger value="buttons">Buttons</TabsTrigger>
<TabsTrigger value="links">Links</TabsTrigger>
<TabsTrigger value="menus">Menus</TabsTrigger>
<TabsTrigger value="pagination">Pagination</TabsTrigger>
<TabsTrigger value="selects">Selects</TabsTrigger>
<TabsTrigger value="tabs">Tabs</TabsTrigger>
......@@ -62,6 +64,7 @@ const ChakraShowcases = () => {
<BadgesShowcase/>
<ButtonShowcase/>
<LinksShowcase/>
<MenusShowcase/>
<TabsShowcase/>
<PaginationShowcase/>
<SelectsShowcase/>
......
......@@ -8,12 +8,12 @@ import { useAppContext } from 'lib/contexts/app';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import getQueryParamString from 'lib/router/getQueryParamString';
import { publicClient } from 'lib/web3/client';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
import TextAd from 'ui/shared/ad/TextAd';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import EntityTags from 'ui/shared/EntityTags/EntityTags';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery';
import TxAssetFlows from 'ui/tx/TxAssetFlows';
import TxBlobs from 'ui/tx/TxBlobs';
......@@ -52,34 +52,34 @@ const TransactionPageContent = () => {
title: config.features.suave.isEnabled && data?.wrapped ? 'Confidential compute tx details' : 'Details',
component: detailsComponent,
},
txInterpretation.isEnabled && txInterpretation.provider === 'noves' ?
{ id: 'asset_flows', title: 'Asset Flows', component: <TxAssetFlows hash={ hash }/> } :
undefined,
config.features.suave.isEnabled && data?.wrapped ?
{ id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } :
undefined,
{ id: 'token_transfers', title: 'Token transfers', component: <TxTokenTransfer txQuery={ txQuery }/> },
config.features.userOps.isEnabled ?
{ id: 'user_ops', title: 'User operations', component: <TxUserOps txQuery={ txQuery }/> } :
undefined,
{ id: 'internal', title: 'Internal txns', component: <TxInternals txQuery={ txQuery }/> },
config.features.dataAvailability.isEnabled && txQuery.data?.blob_versioned_hashes?.length ?
{ id: 'blobs', title: 'Blobs', component: <TxBlobs txQuery={ txQuery }/> } :
undefined,
{ id: 'logs', title: 'Logs', component: <TxLogs txQuery={ txQuery }/> },
{ id: 'state', title: 'State', component: <TxState txQuery={ txQuery }/> },
{ id: 'raw_trace', title: 'Raw trace', component: <TxRawTrace txQuery={ txQuery }/> },
// txInterpretation.isEnabled && txInterpretation.provider === 'noves' ?
// { id: 'asset_flows', title: 'Asset Flows', component: <TxAssetFlows hash={ hash }/> } :
// undefined,
// config.features.suave.isEnabled && data?.wrapped ?
// { id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } :
// undefined,
// { id: 'token_transfers', title: 'Token transfers', component: <TxTokenTransfer txQuery={ txQuery }/> },
// config.features.userOps.isEnabled ?
// { id: 'user_ops', title: 'User operations', component: <TxUserOps txQuery={ txQuery }/> } :
// undefined,
// { id: 'internal', title: 'Internal txns', component: <TxInternals txQuery={ txQuery }/> },
// config.features.dataAvailability.isEnabled && txQuery.data?.blob_versioned_hashes?.length ?
// { id: 'blobs', title: 'Blobs', component: <TxBlobs txQuery={ txQuery }/> } :
// undefined,
// { id: 'logs', title: 'Logs', component: <TxLogs txQuery={ txQuery }/> },
// { id: 'state', title: 'State', component: <TxState txQuery={ txQuery }/> },
// { id: 'raw_trace', title: 'Raw trace', component: <TxRawTrace txQuery={ txQuery }/> },
].filter(Boolean);
})();
const tabIndex = useTabIndexFromQuery(tabs);
const tags = (
<EntityTags
isLoading={ isPlaceholderData }
tags={ data?.transaction_tag ? [ { slug: data.transaction_tag, name: data.transaction_tag, tagType: 'private_tag' as const } ] : [] }
/>
);
// const tags = (
// <EntityTags
// isLoading={ isPlaceholderData }
// tags={ data?.transaction_tag ? [ { slug: data.transaction_tag, name: data.transaction_tag, tagType: 'private_tag' as const, ordinal: 10 } ] : [] }
// />
// );
const backLink = React.useMemo(() => {
const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/txs');
......@@ -100,7 +100,7 @@ const TransactionPageContent = () => {
if (isPlaceholderData && !showDegradedView) {
return (
<>
<TabsSkeleton tabs={ tabs } mt={ 6 }/>
<RoutedTabsSkeleton tabs={ tabs } mt={ 6 }/>
{ tabs[tabIndex]?.component }
</>
);
......@@ -121,7 +121,7 @@ const TransactionPageContent = () => {
<PageTitle
title="Transaction details"
backLink={ backLink }
contentAfter={ tags }
// contentAfter={ tags }
secondRow={ titleSecondRow }
/>
{ content }
......
......@@ -5,6 +5,8 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
......@@ -12,12 +14,12 @@ import getNetworkValidationActionText from 'lib/networks/getNetworkValidationAct
import getQueryParamString from 'lib/router/getQueryParamString';
import { TX } from 'stubs/tx';
import { generateListStub } from 'stubs/utils';
import { Link } from 'toolkit/chakra/link';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import TxsStats from 'ui/txs/TxsStats';
import TxsWatchlist from 'ui/txs/TxsWatchlist';
......@@ -162,15 +164,15 @@ const Transactions = () => {
return (
<Flex alignItems="center" gap={ 6 }>
{ isAdvancedFilterEnabled && (
<LinkInternal
href="/advanced-filter"
<Link
href={ route({ pathname: '/advanced-filter' }) }
alignItems="center"
display="flex"
gap={ 1 }
>
<IconSvg name="filter" boxSize={ 5 }/>
Advanced filter
</LinkInternal>
</Link>
) }
{ pagination.isVisible && <Pagination my={ 1 } { ...pagination }/> }
</Flex>
......@@ -186,7 +188,7 @@ const Transactions = () => {
<TxsStats/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ rightSlot }
stickyEnabled={ !isMobile }
/>
......
import { Box, IconButton, MenuButton, MenuList, chakra } from '@chakra-ui/react';
import { Box, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -7,8 +7,9 @@ import type { ItemProps } from './types';
import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index';
import getQueryParamString from 'lib/router/getQueryParamString';
import Menu from 'ui/shared/chakra/Menu';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { IconButton } from 'toolkit/chakra/icon-button';
import { MenuContent, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu';
import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
......@@ -17,6 +18,7 @@ import PrivateTagMenuItem from './items/PrivateTagMenuItem';
import PublicTagMenuItem from './items/PublicTagMenuItem';
import TokenInfoMenuItem from './items/TokenInfoMenuItem';
// TODO @tom2drum fix account modals
interface Props {
isLoading?: boolean;
className?: string;
......@@ -44,14 +46,14 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
render: (props: ItemProps) => <MetadataUpdateMenuItem { ...props }/>,
enabled: isTokenInstancePage && showUpdateMetadataItem,
},
{
render: (props: ItemProps) => <TokenInfoMenuItem { ...props }/>,
enabled: config.features.account.isEnabled && isTokenPage && config.features.addressVerification.isEnabled && !userWithoutEmail,
},
{
render: (props: ItemProps) => <PrivateTagMenuItem { ...props } entityType={ isTxPage ? 'tx' : 'address' }/>,
enabled: config.features.account.isEnabled,
},
// {
// render: (props: ItemProps) => <TokenInfoMenuItem { ...props }/>,
// enabled: config.features.account.isEnabled && isTokenPage && config.features.addressVerification.isEnabled && !userWithoutEmail,
// },
// {
// render: (props: ItemProps) => <PrivateTagMenuItem { ...props } entityType={ isTxPage ? 'tx' : 'address' }/>,
// enabled: config.features.account.isEnabled,
// },
{
render: (props: ItemProps) => <PublicTagMenuItem { ...props }/>,
enabled: config.features.account.isEnabled && !isTxPage && config.features.publicTagsSubmission.isEnabled,
......@@ -63,7 +65,7 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
}
if (isLoading) {
return <Skeleton w="36px" h="32px" borderRadius="base" className={ className }/>;
return <Skeleton loading w="36px" h="32px" borderRadius="base" className={ className }/>;
}
if (items.length === 1) {
......@@ -75,26 +77,20 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
}
return (
<Menu>
<MenuButton
as={ IconButton }
className={ className }
size="sm"
variant="outline"
colorScheme="gray"
px="7px"
onClick={ handleButtonClick }
icon={ <IconSvg name="dots" boxSize="18px"/> }
aria-label="Show address menu"
/>
<MenuList minWidth="180px" zIndex="popover">
<MenuRoot>
<MenuTrigger asChild>
<IconButton variant="dropdown" size="sm" className={ className } onClick={ handleButtonClick } aria-label="Show address menu">
<IconSvg name="dots" boxSize="18px"/>
</IconButton>
</MenuTrigger>
<MenuContent>
{ items.map(({ render }, index) => (
<React.Fragment key={ index }>
{ render({ type: 'menu_item', hash }) }
</React.Fragment>
)) }
</MenuList>
</Menu>
</MenuContent>
</MenuRoot>
);
};
......
......@@ -8,7 +8,7 @@ import { useMetadataUpdateContext } from 'ui/tokenInstance/contexts/metadataUpda
import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';
const MetadataUpdateMenuItem = ({ type, className }: ItemProps) => {
const MetadataUpdateMenuItem = ({ type }: ItemProps) => {
const { status, setStatus } = useMetadataUpdateContext() || {};
......@@ -24,15 +24,14 @@ const MetadataUpdateMenuItem = ({ type, className }: ItemProps) => {
label="Refresh metadata"
icon="refresh"
onClick={ handleClick }
className={ className }
isDisabled={ status === 'WAITING_FOR_RESPONSE' }
/>
);
}
case 'menu_item': {
return (
<MenuItem className={ className } onClick={ handleClick } isDisabled={ status === 'WAITING_FOR_RESPONSE' }>
<IconSvg name="refresh" boxSize={ 5 } mr={ 2 }/>
<MenuItem onClick={ handleClick } isDisabled={ status === 'WAITING_FOR_RESPONSE' } value="refresh-metadata">
<IconSvg name="refresh" boxSize={ 5 }/>
<span>Refresh metadata</span>
</MenuItem>
);
......
import { useDisclosure } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -9,6 +8,7 @@ import type { Transaction } from 'types/api/transaction';
import { getResourceKey } from 'lib/api/useApiQuery';
import getPageType from 'lib/mixpanel/getPageType';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AddressModal from 'ui/privateTags/AddressModal/AddressModal';
import TransactionModal from 'ui/privateTags/TransactionModal/TransactionModal';
import IconSvg from 'ui/shared/IconSvg';
......@@ -21,7 +21,7 @@ interface Props extends ItemProps {
entityType?: 'address' | 'tx';
}
const PrivateTagMenuItem = ({ className, hash, entityType = 'address', type }: Props) => {
const PrivateTagMenuItem = ({ hash, entityType = 'address', type }: Props) => {
const modal = useDisclosure();
const queryClient = useQueryClient();
const router = useRouter();
......@@ -46,7 +46,7 @@ const PrivateTagMenuItem = ({ className, hash, entityType = 'address', type }: P
const pageType = getPageType(router.pathname);
const modalProps = {
isOpen: modal.isOpen,
isOpen: modal.open,
onClose: modal.onClose,
onSuccess: handleAddPrivateTag,
pageType,
......@@ -58,7 +58,7 @@ const PrivateTagMenuItem = ({ className, hash, entityType = 'address', type }: P
return (
<AuthGuard onAuthSuccess={ modal.onOpen }>
{ ({ onClick }) => (
<ButtonItem label="Add private tag" icon="privattags" onClick={ onClick } className={ className }/>
<ButtonItem label="Add private tag" icon="privattags" onClick={ onClick }/>
) }
</AuthGuard>
);
......@@ -67,7 +67,7 @@ const PrivateTagMenuItem = ({ className, hash, entityType = 'address', type }: P
return (
<AuthGuard onAuthSuccess={ modal.onOpen }>
{ ({ onClick }) => (
<MenuItem className={ className } onClick={ onClick }>
<MenuItem onClick={ onClick } value="add-private-tag">
<IconSvg name="privattags" boxSize={ 6 } mr={ 2 }/>
<span>Add private tag</span>
</MenuItem>
......
......@@ -8,7 +8,7 @@ import IconSvg from 'ui/shared/IconSvg';
import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';
const PublicTagMenuItem = ({ className, hash, type }: ItemProps) => {
const PublicTagMenuItem = ({ hash, type }: ItemProps) => {
const router = useRouter();
const handleClick = React.useCallback(() => {
......@@ -17,11 +17,11 @@ const PublicTagMenuItem = ({ className, hash, type }: ItemProps) => {
switch (type) {
case 'button': {
return <ButtonItem label="Add public tag" icon="publictags" onClick={ handleClick } className={ className }/>;
return <ButtonItem label="Add public tag" icon="publictags" onClick={ handleClick }/>;
}
case 'menu_item': {
return (
<MenuItem className={ className } onClick={ handleClick }>
<MenuItem onClick={ handleClick } value="add-public-tag">
<IconSvg name="publictags" boxSize={ 6 } mr={ 2 }/>
<span>Add public tag</span>
</MenuItem>
......
import { chakra, useDisclosure } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -7,6 +7,7 @@ import type { ItemProps } from '../types';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import IconSvg from 'ui/shared/IconSvg';
import AuthGuard from 'ui/snippets/auth/AuthGuard';
......@@ -15,7 +16,7 @@ import useIsAuth from 'ui/snippets/auth/useIsAuth';
import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';
const TokenInfoMenuItem = ({ className, hash, type }: ItemProps) => {
const TokenInfoMenuItem = ({ hash, type }: ItemProps) => {
const router = useRouter();
const modal = useDisclosure();
const isAuth = useIsAuth();
......@@ -72,7 +73,7 @@ const TokenInfoMenuItem = ({ className, hash, type }: ItemProps) => {
return (
<AuthGuard onAuthSuccess={ onAuthSuccess }>
{ ({ onClick }) => (
<ButtonItem label={ label } icon={ icon } onClick={ onClick } className={ className }/>
<ButtonItem label={ label } icon={ icon } onClick={ onClick }/>
) }
</AuthGuard>
);
......@@ -81,7 +82,7 @@ const TokenInfoMenuItem = ({ className, hash, type }: ItemProps) => {
return (
<AuthGuard onAuthSuccess={ onAuthSuccess }>
{ ({ onClick }) => (
<MenuItem className={ className } onClick={ onClick }>
<MenuItem onClick={ onClick } value="add-token-info">
{ icon }
<chakra.span ml={ 2 }>{ label }</chakra.span>
</MenuItem>
......@@ -98,7 +99,7 @@ const TokenInfoMenuItem = ({ className, hash, type }: ItemProps) => {
<AddressVerificationModal
defaultAddress={ hash }
pageType={ PAGE_TYPE_DICT['/token/[hash]'] }
isOpen={ modal.isOpen }
isOpen={ modal.open }
onClose={ modal.onClose }
onSubmit={ handleVerifiedAddressSubmit }
onAddTokenInfoClick={ handleAddApplicationClick }
......
import { IconButton, Tooltip } from '@chakra-ui/react';
import React from 'react';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Tooltip } from 'toolkit/chakra/tooltip';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
......@@ -14,17 +15,18 @@ interface Props {
const ButtonItem = ({ className, label, onClick, icon, isDisabled }: Props) => {
return (
<Tooltip label={ label } isDisabled={ isDisabled }>
<Tooltip content={ label } disabled={ isDisabled }>
<IconButton
aria-label={ label }
className={ className }
icon={ typeof icon === 'string' ? <IconSvg name={ icon } boxSize={ 6 }/> : icon }
onClick={ onClick }
isDisabled={ isDisabled }
disabled={ isDisabled }
size="sm"
variant="outline"
px="4px"
/>
>
{ typeof icon === 'string' ? <IconSvg name={ icon } boxSize={ 6 }/> : icon }
</IconButton>
</Tooltip>
);
};
......
import { MenuItem as MenuItemChakra } from '@chakra-ui/react';
import React from 'react';
import { MenuItem as MenuItemChakra } from 'toolkit/chakra/menu';
interface Props {
className?: string;
children: React.ReactNode;
onClick: () => void;
value: string;
isDisabled?: boolean;
}
const MenuItem = ({ className, children, onClick, isDisabled }: Props) => {
const MenuItem = ({ className, children, onClick, value, isDisabled }: Props) => {
return (
<MenuItemChakra className={ className } onClick={ onClick } py={ 2 } px={ 4 } isDisabled={ isDisabled }>
<MenuItemChakra className={ className } onClick={ onClick } disabled={ isDisabled } value={ value }>
{ children }
</MenuItemChakra>
);
......
export type ItemType = 'button' | 'menu_item';
export interface ItemProps {
className?: string;
type: ItemType;
hash: string;
}
import { Flex, useColorModeValue, chakra } from '@chakra-ui/react';
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
export type Props = {
......@@ -22,8 +22,6 @@ const ContainerWithScrollY = ({ className, gradientHeight, children, onScrollVis
onScrollVisibilityChange?.(hasScroll);
}, [ gradientHeight, onScrollVisibilityChange ]);
const gradientEndColor = useColorModeValue('white', 'black');
return (
<Flex
flexDirection="column"
......@@ -37,7 +35,7 @@ const ContainerWithScrollY = ({ className, gradientHeight, children, onScrollVis
left: 0,
right: '20px',
height: `${ gradientHeight }px`,
bgGradient: `linear(to-b, transparent, ${ gradientEndColor })`,
bgGradient: { _light: `linear(to-b, transparent, {colors.white}`, _dark: `linear(to-b, transparent, {colors.black})` },
} : undefined }
pr={ hasScroll ? 5 : 0 }
pb={ hasScroll ? `${ gradientHeight }px` : 0 }
......
import type { ResponsiveValue } from '@chakra-ui/react';
import type { GridItemProps } from '@chakra-ui/react';
import { GridItem, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
className?: string;
id?: string;
colSpan?: ResponsiveValue<number | 'auto'>;
colSpan?: GridItemProps['colSpan'];
}
const DetailsInfoItemDivider = ({ className, id, colSpan }: Props) => {
......
import { createListCollection } from '@chakra-ui/react';
import React from 'react';
import hexToUtf8 from 'lib/hexToUtf8';
import { SelectItem, SelectContent, SelectValueText, SelectRoot, SelectControl } from 'toolkit/chakra/select';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
import Select from 'ui/shared/select/Select';
const OPTIONS = [
{ label: 'Hex', value: 'Hex' as const },
{ label: 'UTF-8', value: 'UTF-8' as const },
];
const collection = createListCollection({
items: OPTIONS,
});
export type DataType = (typeof OPTIONS)[number]['value'];
interface Props {
......@@ -22,17 +27,30 @@ interface Props {
const RawInputData = ({ hex, rightSlot: rightSlotProp, defaultDataType = 'Hex', isLoading, minHeight }: Props) => {
const [ selectedDataType, setSelectedDataType ] = React.useState<DataType>(defaultDataType);
const handleValueChange = React.useCallback(({ value }: { value: Array<string> }) => {
setSelectedDataType(value[0] as DataType);
}, []);
const rightSlot = (
<>
<Select
options={ OPTIONS }
<SelectRoot
name="data-type"
defaultValue={ defaultDataType }
onChange={ setSelectedDataType }
isLoading={ isLoading }
w="90px"
mr="auto"
/>
collection={ collection }
variant="outline"
defaultValue={ [ defaultDataType ] }
onValueChange={ handleValueChange }
>
<SelectControl w="100px" mr="auto" loading={ isLoading }>
<SelectValueText placeholder="Select framework"/>
</SelectControl>
<SelectContent>
{ collection.items.map((item) => (
<SelectItem item={ item } key={ item.value }>
{ item.label }
</SelectItem>
)) }
</SelectContent>
</SelectRoot>
{ rightSlotProp }
</>
);
......
import { Alert, chakra } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Alert } from 'toolkit/chakra/alert';
interface Props {
isLoading?: boolean;
......@@ -15,9 +15,7 @@ const TestnetWarning = ({ isLoading, className }: Props) => {
}
return (
<Skeleton className={ className } isLoaded={ !isLoading }>
<Alert status="warning">This is a testnet transaction only</Alert>
</Skeleton>
<Alert status="warning" loading={ isLoading } className={ className }>This is a testnet transaction only</Alert>
);
};
......
import type { MenuProps } from '@chakra-ui/react';
// eslint-disable-next-line no-restricted-imports
import { Menu as MenuBase } from '@chakra-ui/react';
import React from 'react';
const Menu = (props: MenuProps) => {
return <MenuBase gutter={ 4 } { ...props }/>;
};
export default React.memo(Menu);
import { Divider, Flex, VStack } from '@chakra-ui/react';
import { Separator, Flex, VStack } from '@chakra-ui/react';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import { Badge } from 'toolkit/chakra/badge';
import { Skeleton } from 'toolkit/chakra/skeleton';
interface Props {
methodId: string;
......@@ -21,7 +21,7 @@ const Item = ({ label, children, isLoading }: { label: string; children: React.R
flexDir={{ base: 'column', lg: 'row' }}
alignItems={{ base: 'flex-start', lg: 'center' }}
>
<Skeleton fontWeight={ 600 } w={{ base: 'auto', lg: '80px' }} flexShrink={ 0 } isLoaded={ !isLoading }>
<Skeleton fontWeight={ 600 } w={{ base: 'auto', lg: '80px' }} flexShrink={ 0 } loading={ isLoading }>
{ label }
</Skeleton >
{ children }
......@@ -33,20 +33,19 @@ const LogDecodedInputDataHeader = ({ methodId, methodCall, isLoading, rightSlot
return (
<VStack
align="flex-start"
divider={ <Divider/> }
fontSize="sm"
lineHeight={ 5 }
separator={ <Separator/> }
textStyle="sm"
flexGrow={ 1 }
w="100%"
>
<Flex columnGap={ 2 } w="100%">
<Item label="Method id" isLoading={ isLoading }>
<Tag isLoading={ isLoading }>{ methodId }</Tag>
<Badge loading={ isLoading }>{ methodId }</Badge>
</Item>
{ rightSlot }
</Flex>
<Item label="Call" isLoading={ isLoading }>
<Skeleton isLoaded={ !isLoading } whiteSpace="pre-wrap" w="100%">{ methodCall }</Skeleton>
<Skeleton loading={ isLoading } whiteSpace="pre-wrap" w="100%">{ methodCall }</Skeleton>
</Item>
</VStack>
);
......
import { Flex, Grid, useColorModeValue } from '@chakra-ui/react';
import { Flex, Grid } from '@chakra-ui/react';
import React from 'react';
import type { DecodedInput } from 'types/api/decodedInput';
import type { ArrayElement } from 'types/utils';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -22,7 +22,7 @@ const HeaderItem = ({ children, isLoading }: { children: React.ReactNode; isLoad
display="inline-block"
width="fit-content"
height="fit-content"
isLoaded={ !isLoading }
loading={ isLoading }
>
{ children }
</Skeleton>
......@@ -63,15 +63,14 @@ const Row = ({ name, type, indexed, value, isLoading }: ArrayElement<DecodedInpu
<TruncatedValue value={ name } isLoading={ isLoading }/>
<TruncatedValue value={ type } isLoading={ isLoading }/>
{ indexed !== undefined && (
<Skeleton isLoaded={ !isLoading } display="inline-block">{ indexed ? 'true' : 'false' }</Skeleton>
<Skeleton loading={ isLoading } display="inline-block">{ indexed ? 'true' : 'false' }</Skeleton>
) }
<Skeleton isLoaded={ !isLoading } display="inline-block">{ content }</Skeleton>
<Skeleton loading={ isLoading } display="inline-block">{ content }</Skeleton>
</>
);
};
const LogDecodedInputDataTable = ({ data, isLoading }: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const hasIndexed = data.some(({ indexed }) => indexed !== undefined);
const gridTemplateColumnsBase = hasIndexed ?
......@@ -84,9 +83,8 @@ const LogDecodedInputDataTable = ({ data, isLoading }: Props) => {
return (
<Grid
gridTemplateColumns={{ base: gridTemplateColumnsBase, lg: gridTemplateColumnsLg }}
fontSize="sm"
lineHeight={ 5 }
bgColor={ bgColor }
textStyle="sm"
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
p={ 4 }
mt={ 2 }
w="100%"
......
import { Tooltip, chakra } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
......@@ -13,8 +13,9 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import * as mixpanel from 'lib/mixpanel/index';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import { Badge } from 'toolkit/chakra/badge';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
......@@ -103,7 +104,7 @@ const TxInterpretationElementByType = (
</chakra.span>
);
}
return <chakra.span color="text_secondary" whiteSpace="pre">{ value + ' ' }</chakra.span>;
return <chakra.span color="text.secondary" whiteSpace="pre">{ value + ' ' }</chakra.span>;
}
case 'currency': {
let numberString = '';
......@@ -119,19 +120,19 @@ const TxInterpretationElementByType = (
return <chakra.span>{ numberString + ' ' }</chakra.span>;
}
case 'timestamp': {
return <chakra.span color="text_secondary" whiteSpace="pre">{ dayjs(Number(value) * 1000).format('MMM DD YYYY') }</chakra.span>;
return <chakra.span color="text.secondary" whiteSpace="pre">{ dayjs(Number(value) * 1000).format('MMM DD YYYY') }</chakra.span>;
}
case 'method': {
return (
<Tag
<Badge
colorScheme={ value === 'Multicall' ? 'teal' : 'gray' }
isTruncated
truncated
ml={ 1 }
mr={ 2 }
verticalAlign="text-top"
>
{ value }
</Tag>
</Badge>
);
}
}
......@@ -155,9 +156,9 @@ const TxInterpretation = ({ summary, isLoading, addressDataMap, className }: Pro
const chunks = getStringChunks(intermediateResult);
return (
<Skeleton isLoaded={ !isLoading } className={ className } fontWeight={ 500 } whiteSpace="pre-wrap" >
<Tooltip label="Transaction summary">
<IconSvg name="lightning" boxSize={ 5 } color="text_secondary" mr={ 1 } verticalAlign="text-top"/>
<Skeleton loading={ isLoading } className={ className } fontWeight={ 500 } whiteSpace="pre-wrap" >
<Tooltip content="Transaction summary">
<IconSvg name="lightning" boxSize={ 5 } color="text.secondary" mr={ 1 } verticalAlign="text-top"/>
</Tooltip>
{ chunks.map((chunk, index) => {
let content = null;
......@@ -175,7 +176,7 @@ const TxInterpretation = ({ summary, isLoading, addressDataMap, className }: Pro
}
return (
<chakra.span key={ chunk + index }>
<chakra.span color="text_secondary">{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</chakra.span>
<chakra.span color="text.secondary">{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</chakra.span>
{ index < variablesNames.length && content }
</chakra.span>
);
......
......@@ -2,6 +2,7 @@ import { Box } from '@chakra-ui/react';
import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
......@@ -48,6 +49,14 @@ const AlertsShowcase = () => {
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>As Link</SectionSubHeader>
<SamplesStack>
<Sample>
<Link href="/" asChild>
<Alert status="info" title="Info"> Alert content </Alert>
</Link>
</Sample>
</SamplesStack>
<SectionSubHeader>Inside table (SocketNewItemsNotice)</SectionSubHeader>
<SamplesStack>
<Sample label="loading">
......
......@@ -2,10 +2,11 @@ import React from 'react';
import { Button } from 'toolkit/chakra/button';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Link } from 'toolkit/chakra/link';
import { PopoverContent, PopoverRoot, PopoverTrigger, PopoverBody } from 'toolkit/chakra/popover';
import { BACKGROUND_DEFAULT } from 'ui/home/HeroBanner';
import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
const ButtonShowcase = () => {
return (
......@@ -182,6 +183,18 @@ const ButtonShowcase = () => {
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>As Link</SectionSubHeader>
<SamplesStack>
<Sample>
<Link href="/" asChild>
<Button>I am link</Button>
</Link>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
......
import React from 'react';
import { IconButton } from 'toolkit/chakra/icon-button';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu';
import IconSvg from 'ui/shared/IconSvg';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
const MenusShowcase = () => {
return (
<Container value="menus">
<Section>
<SectionHeader>Variant</SectionHeader>
<SamplesStack>
<Sample label="variant: subtle">
<MenuRoot>
<MenuTrigger asChild>
<IconButton variant="dropdown" size="sm">
<IconSvg name="dots" boxSize="18px"/>
</IconButton>
</MenuTrigger>
<MenuContent>
<MenuItem value="refresh-metadata">Refresh metadata</MenuItem>
<MenuItem value="add-token-info">Add token info</MenuItem>
<MenuItem value="add-private-tag">Add private tag</MenuItem>
<MenuItem value="add-public-tag">Add public tag</MenuItem>
</MenuContent>
</MenuRoot>
<MenuRoot>
<MenuTrigger asChild>
<IconButton variant="dropdown" size="sm" loading>
<IconSvg name="dots" boxSize="18px"/>
</IconButton>
</MenuTrigger>
<MenuContent>
<MenuItem value="refresh-metadata">Refresh metadata</MenuItem>
<MenuItem value="add-token-info">Add token info</MenuItem>
<MenuItem value="add-private-tag">Add private tag</MenuItem>
<MenuItem value="add-public-tag">Add public tag</MenuItem>
</MenuContent>
</MenuRoot>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>Example 1</SectionSubHeader>
</Section>
</Container>
);
};
export default React.memo(MenusShowcase);
import { Flex, Link, useBoolean } from '@chakra-ui/react';
import { Flex } from '@chakra-ui/react';
import React from 'react';
import { Link } from 'toolkit/chakra/link';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
......@@ -10,8 +11,13 @@ interface Props {
const CUT_LENGTH = 2;
// TODO @tom2drum another variant of CutLink
const TxAllowedPeekers = ({ items }: Props) => {
const [ isExpanded, expand ] = useBoolean(false);
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleCutLinkClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
}, []);
return (
<>
......@@ -32,7 +38,7 @@ const TxAllowedPeekers = ({ items }: Props) => {
fontSize="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ expand.toggle }
onClick={ handleCutLinkClick }
>
{ isExpanded ? 'Hide' : 'Show all' }
</Link>
......
......@@ -132,15 +132,15 @@ const TxDetailsDegraded = ({ hash, txQuery }: Props) => {
React.useEffect(() => {
if (!query.isPlaceholderData && hasData) {
txQuery.setRefetchOnError.on();
txQuery.setRefetchEnabled(true);
}
}, [ hasData, query.isPlaceholderData, txQuery ]);
React.useEffect(() => {
return () => {
txQuery.setRefetchOnError.off();
txQuery.setRefetchEnabled(false);
};
}, [ txQuery.setRefetchOnError ]);
}, [ txQuery ]);
if (!query.data) {
if (originalError && isCustomAppError(originalError)) {
......
import { Alert } from '@chakra-ui/react';
import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
import { Link } from 'toolkit/chakra/link';
interface Props {
status: 'error' | 'close';
}
......@@ -10,7 +11,11 @@ const TxSocketAlert = ({ status }: Props) => {
'Connection is lost. Please click here to update transaction info.' :
'An error has occurred while fetching transaction info. Please click here to update.';
return <Alert status="warning" as="a" href={ window.document.location.href }>{ text }</Alert>;
return (
<Link href={ window.document.location.href } asChild>
<Alert status="warning">{ text }</Alert>
</Link>
);
};
export default React.memo(TxSocketAlert);
import { Box, Flex, Link } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import type { AddressParam } from 'types/api/addressParams';
......@@ -7,7 +7,9 @@ import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate';
import { TX_INTERPRETATION } from 'stubs/txInterpretation';
import { Link } from 'toolkit/chakra/link';
import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu';
// TODO @tom2drum fix app action button
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
import { TX_ACTIONS_BLOCK_ID } from 'ui/shared/DetailsActionsWrapper';
......@@ -136,9 +138,9 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => {
mt={{ base: 3, lg: 0 }}
>
{ !hasTag && <AccountActionsMenu isLoading={ isLoading }/> }
{ appActionData && (
{ /* { appActionData && (
<AppActionButton data={ appActionData } txHash={ hash } source="Txn"/>
) }
) } */ }
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 0, lg: 'auto' }}/>
</Flex>
</Box>
......
......@@ -3,7 +3,7 @@ import React from 'react';
import config from 'configs/app';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
interface Props {
......@@ -26,7 +26,7 @@ const TxDetailsFeePerGas = ({ txFee, gasUsed, isLoading }: Props) => {
Fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
<Skeleton loading={ isLoading } mr={ 1 }>
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
</Skeleton>
......
......@@ -6,7 +6,7 @@ import type { TokenInfo } from 'types/api/token';
import config from 'configs/app';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
......@@ -24,7 +24,7 @@ const TxDetailsGasPrice = ({ gasPrice, gasToken, isLoading }: Props) => {
const content = (() => {
if (gasToken) {
return (
<Skeleton isLoaded={ !isLoading } display="flex">
<Skeleton loading={ isLoading } display="flex">
<span>{ BigNumber(gasPrice).dividedBy(WEI).toFixed() }</span>
<TokenEntity token={ gasToken } noCopy onlySymbol w="auto" ml={ 1 }/>
</Skeleton>
......@@ -33,10 +33,10 @@ const TxDetailsGasPrice = ({ gasPrice, gasToken, isLoading }: Props) => {
return (
<>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
<Skeleton loading={ isLoading } mr={ 1 }>
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<Skeleton loading={ isLoading } color="text_secondary">
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
</Skeleton>
</>
......
......@@ -23,8 +23,8 @@ const TxDetailsOther = ({ nonce, type, position, queueIndex }: Props) => {
<Box key="type">
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ type }</Text>
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-1559)</Text> }
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-4844)</Text> }
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } color="text.secondary">(EIP-1559)</Text> }
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } color="text.secondary">(EIP-4844)</Text> }
</Box>
),
queueIndex !== undefined ? (
......@@ -47,10 +47,10 @@ const TxDetailsOther = ({ nonce, type, position, queueIndex }: Props) => {
]
.filter(Boolean)
.map((item, index) => (
<>
<React.Fragment key={ index }>
{ index !== 0 && <TextSeparator/> }
{ item }
</>
</React.Fragment>
))
}
</DetailsInfoItem.Value>
......
import { GridItem, Show, Flex } from '@chakra-ui/react';
import { GridItem, Flex, Box } from '@chakra-ui/react';
import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import { route } from 'nextjs-routes';
import { Link } from 'toolkit/chakra/link';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import TokenTransferSnippet from 'ui/shared/TokenTransferSnippet/TokenTransferSnippet';
interface Props {
data: Array<TokenTransfer>;
txHash: string;
......@@ -61,12 +60,12 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => {
}) }
{ isOverflow && (
<>
<Show above="lg" ssr={ false }><GridItem></GridItem></Show>
<Box hideBelow="lg"><GridItem></GridItem></Box>
<GridItem fontSize="sm" alignItems="center" display="inline-flex" pl={{ base: '28px', lg: 0 }}>
<IconSvg name="token" boxSize={ 6 }/>
<LinkInternal href={ viewAllUrl }>
<Link href={ viewAllUrl }>
View all
</LinkInternal>
</Link>
</GridItem>
</>
) }
......
import { Button } from '@chakra-ui/react';
import React from 'react';
import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2';
import config from 'configs/app';
import { Button } from 'toolkit/chakra/button';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
......@@ -71,6 +71,7 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
}
})();
// TODO @tom2drum Button as <a> tags
const rightSlot = hasClaimButton ? (
<Button
variant="outline"
......
......@@ -3,17 +3,13 @@ import {
Grid,
GridItem,
Text,
Link,
Spinner,
Flex,
Tooltip,
chakra,
useColorModeValue,
HStack,
} from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import { scroller, Element } from 'react-scroll';
import { SCROLL_L2_BLOCK_STATUSES } from 'types/api/scrollL2';
import type { Transaction } from 'types/api/transaction';
......@@ -29,8 +25,11 @@ import * as arbitrum from 'lib/rollups/arbitrum';
import { MESSAGE_DESCRIPTIONS } from 'lib/tx/arbitrumMessageStatusDescription';
import getConfirmationDuration from 'lib/tx/getConfirmationDuration';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import { Badge } from 'toolkit/chakra/badge';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import CutLink from 'toolkit/components/CutLink/CutLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import CurrencyValue from 'ui/shared/CurrencyValue';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
......@@ -77,21 +76,12 @@ interface Props {
const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleCutClick = React.useCallback(() => {
const handleCutLinkClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
scroller.scrollTo('TxInfo__cutLink', {
duration: 500,
smooth: true,
});
}, []);
const executionSuccessIconColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const showAssociatedL1Tx = React.useCallback(() => {
setIsExpanded(true);
scroller.scrollTo('TxInfo__cutLink', {
duration: 500,
smooth: true,
});
}, []);
if (!data) {
......@@ -102,24 +92,24 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
...data.from.private_tags || [],
...data.from.public_tags || [],
...data.from.watchlist_names || [],
].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>);
].map((tag) => <Badge key={ tag.label }>{ tag.display_name }</Badge>);
const toAddress = data.to ? data.to : data.created_contract;
const addressToTags = [
...toAddress?.private_tags || [],
...toAddress?.public_tags || [],
...toAddress?.watchlist_names || [],
].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>);
].map((tag) => <Badge key={ tag.label }>{ tag.display_name }</Badge>);
const executionSuccessBadge = toAddress?.is_contract && data.result === 'success' ? (
<Tooltip label="Contract execution completed">
<Tooltip content="Contract execution completed">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
<IconSvg name="status/success" boxSize={ 4 } color={ executionSuccessIconColor } cursor="pointer"/>
<IconSvg name="status/success" boxSize={ 4 } color={{ _light: 'blackAlpha.800', _dark: 'whiteAlpha.800' }} cursor="pointer"/>
</chakra.span>
</Tooltip>
) : null;
const executionFailedBadge = toAddress?.is_contract && Boolean(data.status) && data.result !== 'success' ? (
<Tooltip label="Error occurred during contract execution">
<Tooltip content="Error occurred during contract execution">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
<IconSvg name="status/error" boxSize={ 4 } color="error" cursor="pointer"/>
</chakra.span>
......@@ -151,7 +141,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
{ data.status === null && <Spinner mr={ 2 } size="sm" flexShrink={ 0 }/> }
<Skeleton isLoaded={ !isLoading } overflow="hidden">
<Skeleton loading={ isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.hash }/>
</Skeleton>
<CopyToClipboard text={ data.hash } isLoading={ isLoading }/>
......@@ -178,13 +168,13 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<DetailsInfoItem.Value>
<TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/>
{ data.method && (
<Tag colorScheme={ data.method === 'Multicall' ? 'teal' : 'gray' } isLoading={ isLoading } isTruncated ml={ 3 }>
<Badge colorScheme={ data.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated ml={ 3 }>
{ data.method }
</Tag>
</Badge>
) }
{ data.arbitrum?.contains_message && (
<Skeleton isLoaded={ !isLoading } onClick={ showAssociatedL1Tx }>
<Link isTruncated ml={ 3 }>
<Skeleton loading={ isLoading } onClick={ showAssociatedL1Tx }>
<Link truncate ml={ 3 }>
{ data.arbitrum?.contains_message === 'incoming' ? 'Incoming message' : 'Outgoing message' }
</Link>
</Skeleton>
......@@ -296,7 +286,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
{ Boolean(data.confirmations) && (
<>
<TextSeparator color="gray.500"/>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<Skeleton loading={ isLoading } color="text_secondary">
<span>{ data.confirmations } Block confirmations</span>
</Skeleton>
</>
......@@ -340,7 +330,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
isLoading={ isLoading }
number={ data.zksync.batch_number }
/>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
) : <Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailsInfoItem.Value>
</>
) }
......@@ -356,7 +346,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<DetailsInfoItem.Value>
{ data.arbitrum.batch_number ?
<BatchEntityL2 isLoading={ isLoading } number={ data.arbitrum.batch_number }/> :
<Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
<Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailsInfoItem.Value>
</>
) }
......@@ -374,7 +364,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
{ data.confirmation_duration && (
<>
<TextSeparator color="gray.500"/>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<Skeleton loading={ isLoading } color="text_secondary">
<span>{ getConfirmationDuration(data.confirmation_duration) }</span>
</Skeleton>
</>
......@@ -521,7 +511,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
Sequence tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Skeleton isLoaded={ !isLoading } overflow="hidden">
<Skeleton loading={ isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/>
</Skeleton>
<CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/>
......@@ -538,7 +528,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
Verify tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Skeleton isLoaded={ !isLoading } overflow="hidden">
<Skeleton loading={ isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_verify_hash }/>
</Skeleton>
<CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/>
......@@ -629,9 +619,9 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
Gas usage & limit by txn
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<TextSeparator/>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton>
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton>
<Utilization ml={ 4 } value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
......@@ -644,7 +634,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
Gas used for L1
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l1 || 0).toFormat() }</Skeleton>
<Skeleton loading={ isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l1 || 0).toFormat() }</Skeleton>
<TextSeparator/>
<Utilization
ml={ 4 }
......@@ -660,7 +650,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
Gas used for L2
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l2 || 0).toFormat() }</Skeleton>
<Skeleton loading={ isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l2 || 0).toFormat() }</Skeleton>
<TextSeparator/>
<Utilization
ml={ 4 }
......@@ -680,7 +670,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
L1 Gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.scroll?.l1_gas_used || 0).toFormat() }</Skeleton>
<Skeleton loading={ isLoading }>{ BigNumber(data.scroll?.l1_gas_used || 0).toFormat() }</Skeleton>
</DetailsInfoItem.Value>
</>
) }
......@@ -700,21 +690,21 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.base_fee_per_gas && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
{ (data.max_fee_per_gas || data.max_priority_fee_per_gas) && <TextSeparator/> }
</Skeleton>
) }
{ data.max_fee_per_gas && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
{ data.max_priority_fee_per_gas && <TextSeparator/> }
</Skeleton>
) }
{ data.max_priority_fee_per_gas && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Skeleton>
......@@ -751,7 +741,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
<Text color="text.secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
</DetailsInfoItem.Value>
</>
) }
......@@ -793,135 +783,117 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
) }
<TxInfoScrollFees data={ data } isLoading={ isLoading }/>
<GridItem colSpan={{ base: undefined, lg: 2 }}>
<Element name="TxInfo__cutLink">
<Skeleton isLoaded={ !isLoading } mt={ 6 } display="inline-block">
<Link
display="inline-block"
fontSize="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ handleCutClick }
<CutLink loading={ isLoading } mt={ 6 } gridColumn={{ base: undefined, lg: '1 / 3' }} isExpanded={ isExpanded } onClick={ handleCutLinkClick }>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
{ data.arbitrum?.contains_message && data.arbitrum?.message_related_info && (
<>
<DetailsInfoItem.Label
hint={ data.arbitrum.contains_message === 'incoming' ?
'The hash of the transaction that originated the message from the base layer' :
'The hash of the transaction that completed the message on the base layer'
}
>
{ isExpanded ? 'Hide details' : 'View details' }
</Link>
</Skeleton>
</Element>
</GridItem>
{ data.arbitrum.contains_message === 'incoming' ? 'Originating L1 txn hash' : 'Completion L1 txn hash' }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.arbitrum.message_related_info.associated_l1_transaction ?
<TxEntityL1 hash={ data.arbitrum.message_related_info.associated_l1_transaction }/> : (
<HStack gap={ 2 }>
<Text color="text_secondary">{ data.arbitrum.message_related_info.message_status }</Text>
<Hint label={ MESSAGE_DESCRIPTIONS[data.arbitrum.message_related_info.message_status] }/>
</HStack>
)
}
</DetailsInfoItem.Value>
</>
) }
{ isExpanded && (
<>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
{ (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
{ data.blob_gas_used && data.blob_gas_price && (
<>
<DetailsInfoItem.Label
hint="Blob fee for this transaction"
>
Blob fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue
value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
isLoading={ isLoading }
/>
</DetailsInfoItem.Value>
</>
) }
{ data.arbitrum?.contains_message && data.arbitrum?.message_related_info && (
<>
<DetailsInfoItem.Label
hint={ data.arbitrum.contains_message === 'incoming' ?
'The hash of the transaction that originated the message from the base layer' :
'The hash of the transaction that completed the message on the base layer'
}
>
{ data.arbitrum.contains_message === 'incoming' ? 'Originating L1 txn hash' : 'Completion L1 txn hash' }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.arbitrum.message_related_info.associated_l1_transaction ?
<TxEntityL1 hash={ data.arbitrum.message_related_info.associated_l1_transaction }/> : (
<HStack gap={ 2 }>
<Text color="text_secondary">{ data.arbitrum.message_related_info.message_status }</Text>
<Hint label={ MESSAGE_DESCRIPTIONS[data.arbitrum.message_related_info.message_status] }/>
</HStack>
)
}
</DetailsInfoItem.Value>
</>
) }
{ data.blob_gas_used && (
<>
<DetailsInfoItem.Label
hint="Amount of gas used by the blobs in this transaction"
>
Blob gas usage
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.blob_gas_used).toFormat() }
</DetailsInfoItem.Value>
</>
) }
{ (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
{ data.blob_gas_used && data.blob_gas_price && (
<>
<DetailsInfoItem.Label
hint="Blob fee for this transaction"
>
Blob fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue
value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
isLoading={ isLoading }
/>
</DetailsInfoItem.Value>
</>
) }
{ data.blob_gas_used && (
<>
<DetailsInfoItem.Label
hint="Amount of gas used by the blobs in this transaction"
>
Blob gas usage
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.blob_gas_used).toFormat() }
</DetailsInfoItem.Value>
</>
) }
{ (data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
<DetailsInfoItem.Label
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
>
{ `Blob gas fees (${ currencyUnits.gwei })` }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.blob_gas_price && (
<Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
) }
{ (data.max_fee_per_blob_gas && data.blob_gas_price) && <TextSeparator/> }
{ data.max_fee_per_blob_gas && (
<>
<Text as="span" fontWeight="500" whiteSpace="pre">Max: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</>
) }
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItemDivider/>
</>
) }
{ (data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
<DetailsInfoItem.Label
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
>
{ `Blob gas fees (${ currencyUnits.gwei })` }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.blob_gas_price && (
<Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
) }
{ (data.max_fee_per_blob_gas && data.blob_gas_price) && <TextSeparator/> }
{ data.max_fee_per_blob_gas && (
<>
<Text as="span" fontWeight="500" whiteSpace="pre">Max: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</>
) }
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItemDivider/>
</>
) }
<TxDetailsOther nonce={ data.nonce } type={ data.type } position={ data.position } queueIndex={ data.scroll?.queue_index }/>
<TxDetailsOther nonce={ data.nonce } type={ data.type } position={ data.position } queueIndex={ data.scroll?.queue_index }/>
<DetailsInfoItem.Label
hint="Binary data included with the transaction. See logs tab for additional info"
>
Raw input
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawInputData hex={ data.raw_input } defaultDataType={ data.zilliqa?.is_scilla ? 'UTF-8' : 'Hex' }/>
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
hint="Binary data included with the transaction. See logs tab for additional info"
>
Raw input
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawInputData hex={ data.raw_input } defaultDataType={ data.zilliqa?.is_scilla ? 'UTF-8' : 'Hex' }/>
</DetailsInfoItem.Value>
{ data.decoded_input && (
<>
<DetailsInfoItem.Label
hint="Decoded input data"
>
Decoded input data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem.Value>
</>
) }
{ data.decoded_input && (
<>
<DetailsInfoItem.Label
hint="Decoded input data"
>
Decoded input data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem.Value>
</>
) }
{ data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> }
</>
) }
{ data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> }
</CutLink>
</Grid>
);
};
......
......@@ -6,7 +6,7 @@ import type { Transaction } from 'types/api/transaction';
import { WEI_IN_GWEI } from 'lib/consts';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import CurrencyValue from 'ui/shared/CurrencyValue';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TextSeparator from 'ui/shared/TextSeparator';
......@@ -68,7 +68,7 @@ export const TxInfoScrollFees = ({ data, isLoading }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue
value={ data.scroll?.l1_fee_commit_scalar }
value={ String(data.scroll?.l1_fee_commit_scalar) }
currency={ currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
......@@ -86,9 +86,9 @@ export const TxInfoScrollFees = ({ data, isLoading }: Props) => {
L1 Fee Overhead
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<CurrencyValue
value={ data.scroll?.l1_fee_overhead }
value={ String(data.scroll?.l1_fee_overhead) }
currency={ currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
......@@ -107,13 +107,13 @@ export const TxInfoScrollFees = ({ data, isLoading }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.scroll?.l1_base_fee !== undefined && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.scroll?.l1_base_fee || 0).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Skeleton>
) }
{ data.scroll?.l1_fee_scalar !== undefined && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<TextSeparator/>
<Text as="span" fontWeight="500">Scalar: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.scroll?.l1_fee_scalar || 0).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
......@@ -132,13 +132,13 @@ export const TxInfoScrollFees = ({ data, isLoading }: Props) => {
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.scroll?.l1_blob_base_fee !== undefined && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.scroll?.l1_blob_base_fee || 0).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Skeleton>
) }
{ data.scroll?.l1_fee_blob_scalar !== undefined && (
<Skeleton isLoaded={ !isLoading }>
<Skeleton loading={ isLoading }>
<TextSeparator/>
<Text as="span" fontWeight="500">Scalar: </Text>
<Text fontWeight="600" as="span">{ BigNumber(data.scroll?.l1_fee_blob_scalar || 0).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
......
import { Grid, GridItem, Text, useColorModeValue } from '@chakra-ui/react';
import { Grid, GridItem, Text } from '@chakra-ui/react';
import React from 'react';
import type { TransactionRevertReason } from 'types/api/transaction';
......@@ -10,8 +10,6 @@ import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
type Props = TransactionRevertReason;
const TxRevertReason = (props: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
if ('raw' in props) {
if (!HEX_REGEXP.test(props.raw)) {
return <Text>{ props.raw }</Text>;
......@@ -21,7 +19,7 @@ const TxRevertReason = (props: Props) => {
return (
<Grid
bgColor={ bgColor }
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
p={ 4 }
fontSize="sm"
borderRadius="md"
......
import { useBoolean } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
......@@ -22,11 +21,7 @@ const rollupFeature = config.features.rollup;
export type TxQuery = UseQueryResult<Transaction, ResourceError<{ status: number }>> & {
socketStatus: 'close' | 'error' | undefined;
setRefetchOnError: {
on: () => void;
off: () => void;
toggle: () => void;
};
setRefetchEnabled: (value: boolean) => void;
};
interface Params {
......@@ -36,7 +31,7 @@ interface Params {
export default function useTxQuery(params?: Params): TxQuery {
const [ socketStatus, setSocketStatus ] = React.useState<'close' | 'error'>();
const [ isRefetchEnabled, setRefetchEnabled ] = useBoolean(false);
const [ isRefetchEnabled, setRefetchEnabled ] = React.useState(false);
const router = useRouter();
const queryClient = useQueryClient();
......@@ -93,6 +88,6 @@ export default function useTxQuery(params?: Params): TxQuery {
return React.useMemo(() => ({
...queryResult,
socketStatus,
setRefetchOnError: setRefetchEnabled,
setRefetchEnabled,
}), [ queryResult, socketStatus, setRefetchEnabled ]);
}
......@@ -83,8 +83,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
isLoading={ isLoading }
number={ tx.block_number }
noIcon
fontSize="sm"
lineHeight={ 6 }
textStyle="sm"
fontWeight={ 500 }
/>
) }
......
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