Commit a86ee441 authored by tom's avatar tom

tx details tab

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