mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
blobs, pools and block countdown pages
This commit is contained in:
Vendored
+1
@@ -371,6 +371,7 @@
|
||||
"eth_sepolia",
|
||||
"filecoin",
|
||||
"mekong",
|
||||
"neon_devnet",
|
||||
"optimism",
|
||||
"optimism_celestia",
|
||||
"optimism_sepolia",
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from 'react';
|
||||
import type { Props } from 'nextjs/getServerSideProps';
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const Blob = dynamic(() => import('ui/pages/Blob'), { ssr: false });
|
||||
const Blob = dynamic(() => import('ui/pages/Blob'), { ssr: false });
|
||||
|
||||
const Page: NextPage<Props> = (props: Props) => {
|
||||
return (
|
||||
<PageNextJs pathname="/blobs/[hash]" query={ props.query }>
|
||||
{ /* <Blob/> */ }
|
||||
<Blob/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from 'react';
|
||||
import type { Props } from 'nextjs/getServerSideProps';
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const BlockCountdown = dynamic(() => import('ui/pages/BlockCountdown'), { ssr: false });
|
||||
const BlockCountdown = dynamic(() => import('ui/pages/BlockCountdown'), { ssr: false });
|
||||
|
||||
const Page: NextPage<Props> = (props: Props) => {
|
||||
return (
|
||||
<PageNextJs pathname="/block/countdown/[height]" query={ props.query }>
|
||||
{ /* <BlockCountdown/> */ }
|
||||
<BlockCountdown/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from 'react';
|
||||
import type { Props } from 'nextjs/getServerSideProps';
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const BlockCountdownIndex = dynamic(() => import('ui/pages/BlockCountdownIndex'), { ssr: false });
|
||||
const BlockCountdownIndex = dynamic(() => import('ui/pages/BlockCountdownIndex'), { ssr: false });
|
||||
|
||||
const Page: NextPage<Props> = (props: Props) => {
|
||||
return (
|
||||
<PageNextJs pathname="/block/countdown" query={ props.query }>
|
||||
{ /* <BlockCountdownIndex/> */ }
|
||||
<BlockCountdownIndex/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from 'react';
|
||||
import type { Props } from 'nextjs/getServerSideProps';
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const Pool = dynamic(() => import('ui/pages/Pool'), { ssr: false });
|
||||
const Pool = dynamic(() => import('ui/pages/Pool'), { ssr: false });
|
||||
|
||||
const Page: NextPage<Props> = (props: Props) => {
|
||||
return (
|
||||
<PageNextJs pathname="/pools/[hash]" query={ props.query }>
|
||||
{ /* <Pool/> */ }
|
||||
<Pool/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,12 +4,12 @@ import React from 'react';
|
||||
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const Pools = dynamic(() => import('ui/pages/Pools'), { ssr: false });
|
||||
const Pools = dynamic(() => import('ui/pages/Pools'), { ssr: false });
|
||||
|
||||
const Page: NextPage = () => {
|
||||
return (
|
||||
<PageNextJs pathname="/pools">
|
||||
{ /* <Pools/> */ }
|
||||
<Pools/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from 'react';
|
||||
import type { Props } from 'nextjs/getServerSideProps';
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
// const KettleTxs = dynamic(() => import('ui/pages/KettleTxs'), { ssr: false });
|
||||
const KettleTxs = dynamic(() => import('ui/pages/KettleTxs'), { ssr: false });
|
||||
|
||||
const Page: NextPage<Props> = (props: Props) => {
|
||||
return (
|
||||
<PageNextJs pathname="/txs/kettle/[hash]" query={ props.query }>
|
||||
{ /* <KettleTxs/> */ }
|
||||
<KettleTxs/>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCopyToClipboard } from '@uidotdev/usehooks';
|
||||
import React from 'react';
|
||||
|
||||
import { SECOND } from 'lib/consts';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
|
||||
import { useDisclosure } from './useDisclosure';
|
||||
|
||||
@@ -11,12 +12,15 @@ export default function useClipboard(text: string, timeout = SECOND) {
|
||||
const disclosureTimeoutRef = React.useRef<number | null>(null);
|
||||
const [ hasCopied, setHasCopied ] = React.useState(false);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const [ , copyToClipboard ] = useCopyToClipboard();
|
||||
const { open, onOpenChange } = useDisclosure();
|
||||
|
||||
const copy = React.useCallback(() => {
|
||||
copyToClipboard(text);
|
||||
setHasCopied(true);
|
||||
// there is no hover on mobile, so we need to open the disclosure manually after click
|
||||
isMobile && onOpenChange({ open: true });
|
||||
|
||||
disclosureTimeoutRef.current = window.setTimeout(() => {
|
||||
onOpenChange({ open: false });
|
||||
@@ -26,7 +30,7 @@ export default function useClipboard(text: string, timeout = SECOND) {
|
||||
flagTimeoutRef.current = window.setTimeout(() => {
|
||||
setHasCopied(false);
|
||||
}, timeout + 200);
|
||||
}, [ text, copyToClipboard, timeout, onOpenChange ]);
|
||||
}, [ text, copyToClipboard, timeout, onOpenChange, isMobile ]);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
|
||||
@@ -66,8 +66,9 @@ export const recipe = defineSlotRecipe({
|
||||
},
|
||||
popover: {
|
||||
content: {
|
||||
maxW: 'none',
|
||||
bg: 'popover.bg',
|
||||
color: 'popover.fg',
|
||||
color: 'text.primary',
|
||||
p: '4',
|
||||
boxShadow: 'popover',
|
||||
boxShadowColor: 'popover.shadow',
|
||||
|
||||
+33
-16
@@ -1,4 +1,4 @@
|
||||
import { Flex, GridItem, Button } from '@chakra-ui/react';
|
||||
import { createListCollection, Flex, GridItem } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import * as blobUtils from 'lib/blob';
|
||||
@@ -8,10 +8,11 @@ import downloadBlob from 'lib/downloadBlob';
|
||||
import hexToBase64 from 'lib/hexToBase64';
|
||||
import hexToBytes from 'lib/hexToBytes';
|
||||
import hexToUtf8 from 'lib/hexToUtf8';
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { SelectContent, SelectItem, SelectRoot, SelectControl, SelectValueText } from 'toolkit/chakra/select';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import RawDataSnippet from 'ui/shared/RawDataSnippet';
|
||||
import Select from 'ui/shared/select/Select';
|
||||
|
||||
import BlobDataImage from './BlobDataImage';
|
||||
|
||||
@@ -41,7 +42,12 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
|
||||
}, [ data, isLoading ]);
|
||||
|
||||
const isImage = guessedType?.mime?.startsWith('image/');
|
||||
const formats = isImage ? FORMATS : FORMATS.filter((format) => format.value !== 'Image');
|
||||
const collection = React.useMemo(() => {
|
||||
const formats = isImage ? FORMATS : FORMATS.filter((format) => format.value !== 'Image');
|
||||
return createListCollection({
|
||||
items: formats,
|
||||
});
|
||||
}, [ isImage ]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isImage) {
|
||||
@@ -49,6 +55,10 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
|
||||
}
|
||||
}, [ isImage ]);
|
||||
|
||||
const handleFormatChange = React.useCallback(({ value }: { value: Array<string> }) => {
|
||||
setFormat(value[0] as Format);
|
||||
}, []);
|
||||
|
||||
const handleDownloadButtonClick = React.useCallback(() => {
|
||||
const fileBlob = (() => {
|
||||
switch (format) {
|
||||
@@ -102,20 +112,27 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
|
||||
return (
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 2 }}>
|
||||
<Flex alignItems="center" mb={ 3 }>
|
||||
<Skeleton fontWeight={{ base: 700, lg: 500 }} isLoaded={ !isLoading }>
|
||||
<Skeleton fontWeight={{ base: 700, lg: 500 }} loading={ isLoading }>
|
||||
Blob data
|
||||
</Skeleton>
|
||||
<Skeleton ml={ 5 } isLoaded={ !isLoading }>
|
||||
<Select
|
||||
options={ formats }
|
||||
name="format"
|
||||
defaultValue={ format }
|
||||
onChange={ setFormat }
|
||||
isLoading={ isLoading }
|
||||
w="95px"
|
||||
/>
|
||||
</Skeleton>
|
||||
<Skeleton ml="auto" mr={ 3 } isLoaded={ !isLoading }>
|
||||
<SelectRoot
|
||||
collection={ collection }
|
||||
variant="outline"
|
||||
value={ [ format ] }
|
||||
onValueChange={ handleFormatChange }
|
||||
>
|
||||
<SelectControl loading={ isLoading } ml={ 5 } w="100px">
|
||||
<SelectValueText placeholder="Select framework"/>
|
||||
</SelectControl>
|
||||
<SelectContent>
|
||||
{ collection.items.map((item) => (
|
||||
<SelectItem item={ item } key={ item.value }>
|
||||
{ item.label }
|
||||
</SelectItem>
|
||||
)) }
|
||||
</SelectContent>
|
||||
</SelectRoot>
|
||||
<Skeleton ml="auto" mr={ 3 } loading={ isLoading }>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Alert, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { Grid, GridItem } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Blob } from 'types/api/blobs';
|
||||
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import { Alert } from 'toolkit/chakra/alert';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
|
||||
import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
|
||||
@@ -26,7 +26,7 @@ const BlobInfo = ({ data, isLoading }: Props) => {
|
||||
>
|
||||
{ !data.blob_data && (
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }} mb={ 3 }>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Skeleton loading={ isLoading }>
|
||||
<Alert status="warning">This blob is not yet indexed</Alert>
|
||||
</Skeleton>
|
||||
</GridItem>
|
||||
@@ -41,7 +41,7 @@ const BlobInfo = ({ data, isLoading }: Props) => {
|
||||
Proof
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
<Skeleton loading={ isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
{ data.kzg_proof }
|
||||
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
@@ -58,7 +58,7 @@ const BlobInfo = ({ data, isLoading }: Props) => {
|
||||
Commitment
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
<Skeleton loading={ isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
{ data.kzg_commitment }
|
||||
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
@@ -75,7 +75,7 @@ const BlobInfo = ({ data, isLoading }: Props) => {
|
||||
Size, bytes
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
<Skeleton loading={ isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HStack, StackDivider, useColorModeValue } from '@chakra-ui/react';
|
||||
import { HStack, StackSeparator } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { SECOND } from 'lib/consts';
|
||||
@@ -15,8 +15,6 @@ const BlockCountdownTimer = ({ value: initialValue, onFinish }: Props) => {
|
||||
|
||||
const [ value, setValue ] = React.useState(initialValue);
|
||||
|
||||
const bgColor = useColorModeValue('gray.50', 'whiteAlpha.100');
|
||||
|
||||
React.useEffect(() => {
|
||||
const intervalId = window.setInterval(() => {
|
||||
setValue((prev) => {
|
||||
@@ -38,11 +36,11 @@ const BlockCountdownTimer = ({ value: initialValue, onFinish }: Props) => {
|
||||
|
||||
return (
|
||||
<HStack
|
||||
bgColor={ bgColor }
|
||||
bgColor={{ _light: 'gray.50', _dark: 'whiteAlpha.100' }}
|
||||
mt={{ base: 6, lg: 8 }}
|
||||
p={{ base: 3, lg: 4 }}
|
||||
borderRadius="base"
|
||||
divider={ <StackDivider borderColor="border.divider"/> }
|
||||
separator={ <StackSeparator borderColor="border.divider"/> }
|
||||
>
|
||||
<BlockCountdownTimerItem label="Days" value={ periods.days }/>
|
||||
<BlockCountdownTimerItem label="Hours" value={ periods.hours }/>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { Box, Text, Button, Flex } from '@chakra-ui/react';
|
||||
import { Box, Text, Flex } from '@chakra-ui/react';
|
||||
import Script from 'next/script';
|
||||
import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
const easterEggBadgeFeature = config.features.easterEggBadge;
|
||||
|
||||
const CapybaraRunner = () => {
|
||||
@@ -50,7 +52,13 @@ const CapybaraRunner = () => {
|
||||
<Flex flexDirection="column" alignItems="center" justifyContent="center" gap={ 4 } mt={ 10 }>
|
||||
<Text fontSize="2xl" fontWeight="bold">You unlocked a hidden badge!</Text>
|
||||
<Text fontSize="lg" textAlign="center">Congratulations! You’re eligible to claim an epic hidden badge!</Text>
|
||||
<Button as="a" href={ easterEggBadgeFeature.badgeClaimLink } target="_blank">Claim</Button>
|
||||
<Link
|
||||
href={ easterEggBadgeFeature.badgeClaimLink }
|
||||
target="_blank"
|
||||
asChild
|
||||
>
|
||||
<Button>Claim</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
) }
|
||||
</>
|
||||
|
||||
+24
-17
@@ -1,4 +1,4 @@
|
||||
import { Box, Center, Flex, Heading, Image, useColorModeValue, Grid, Button } from '@chakra-ui/react';
|
||||
import { Box, Center, Flex, Grid } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
@@ -9,12 +9,15 @@ import dayjs from 'lib/date/dayjs';
|
||||
import downloadBlob from 'lib/downloadBlob';
|
||||
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import BlockCountdownTimer from 'ui/blockCountdown/BlockCountdownTimer';
|
||||
import createGoogleCalendarLink from 'ui/blockCountdown/createGoogleCalendarLink';
|
||||
import createIcsFileBlob from 'ui/blockCountdown/createIcsFileBlob';
|
||||
import ContentLoader from 'ui/shared/ContentLoader';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import StatsWidget from 'ui/shared/stats/StatsWidget';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
@@ -27,8 +30,6 @@ type Props = {
|
||||
const BlockCountdown = ({ hideCapybaraRunner }: Props) => {
|
||||
const router = useRouter();
|
||||
const height = getQueryParamString(router.query.height);
|
||||
const iconColor = useColorModeValue('gray.300', 'gray.600');
|
||||
const buttonBgColor = useColorModeValue('gray.100', 'gray.700');
|
||||
|
||||
const { data, isPending, isError, error } = useApiQuery('block_countdown', {
|
||||
queryParams: {
|
||||
@@ -70,9 +71,7 @@ const BlockCountdown = ({ hideCapybaraRunner }: Props) => {
|
||||
<Flex columnGap={ 8 } alignItems="flex-start" justifyContent={{ base: 'space-between', lg: undefined }} w="100%">
|
||||
<Box maxW={{ base: 'calc(100% - 65px - 32px)', lg: 'calc(100% - 125px - 32px)' }}>
|
||||
<Heading
|
||||
fontSize={{ base: '18px', lg: '32px' }}
|
||||
lineHeight={{ base: '24px', lg: '40px' }}
|
||||
h={{ base: '24px', lg: '40px' }}
|
||||
level="1"
|
||||
>
|
||||
<TruncatedValue value={ `Block #${ height }` } w="100%"/>
|
||||
</Heading>
|
||||
@@ -81,32 +80,40 @@ const BlockCountdown = ({ hideCapybaraRunner }: Props) => {
|
||||
<Box>{ dayjs().add(Number(data.result.EstimateTimeInSec), 's').format('llll') }</Box>
|
||||
</Box>
|
||||
<Flex columnGap={ 2 } mt={ 3 }>
|
||||
<LinkExternal
|
||||
variant="subtle"
|
||||
fontSize="sm"
|
||||
lineHeight="20px"
|
||||
<Link
|
||||
external
|
||||
variant="underlaid"
|
||||
textStyle="sm"
|
||||
px={ 2 }
|
||||
display="inline-flex"
|
||||
href={ createGoogleCalendarLink({ blockHeight: height, timeFromNow: Number(data.result.EstimateTimeInSec) }) }
|
||||
>
|
||||
<Image src="/static/google_calendar.svg" alt="Google calendar logo" boxSize={ 5 } mr={ 2 }/>
|
||||
<span>Google</span>
|
||||
</LinkExternal>
|
||||
</Link>
|
||||
<Button
|
||||
variant="subtle"
|
||||
fontWeight={ 400 }
|
||||
variant="plain"
|
||||
px={ 2 }
|
||||
size="sm"
|
||||
bgColor={ buttonBgColor }
|
||||
fontWeight="normal"
|
||||
color="link.primary"
|
||||
_hover={{ color: 'link.primary.hover' }}
|
||||
bgColor="link.underlaid.bg"
|
||||
display="inline-flex"
|
||||
onClick={ handleAddToAppleCalClick }
|
||||
>
|
||||
<Image src="/static/apple_calendar.svg" alt="Apple calendar logo" boxSize={ 5 } mr={ 2 }/>
|
||||
<Image src="/static/apple_calendar.svg" alt="Apple calendar logo" boxSize={ 5 }/>
|
||||
<span>Apple</span>
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
<IconSvg name="block_slim" w={{ base: '65px', lg: '125px' }} h={{ base: '75px', lg: '140px' }} color={ iconColor } flexShrink={ 0 }/>
|
||||
<IconSvg
|
||||
name="block_slim"
|
||||
w={{ base: '65px', lg: '125px' }}
|
||||
h={{ base: '75px', lg: '140px' }}
|
||||
color={{ _light: 'gray.300', _dark: 'gray.600' }}
|
||||
flexShrink={ 0 }
|
||||
/>
|
||||
</Flex>
|
||||
{ data.result.EstimateTimeInSec && (
|
||||
<BlockCountdownTimer
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { chakra, Box, Center, Heading, useColorModeValue } from '@chakra-ui/react';
|
||||
import { chakra, Box, Center } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
import FilterInput from 'ui/shared/filters/FilterInput';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
const BlockCountdownIndex = () => {
|
||||
const router = useRouter();
|
||||
const iconColor = useColorModeValue('gray.300', 'gray.600');
|
||||
|
||||
const handleFormSubmit = React.useCallback((event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -21,11 +21,14 @@ const BlockCountdownIndex = () => {
|
||||
|
||||
return (
|
||||
<Center h="100%" justifyContent={{ base: 'flex-start', lg: 'center' }} flexDir="column" textAlign="center" pt={{ base: 8, lg: 0 }}>
|
||||
<IconSvg name="block_countdown" color={ iconColor } w={{ base: '160px', lg: '240px' }} h={{ base: '123px', lg: '184px' }}/>
|
||||
<IconSvg
|
||||
name="block_countdown"
|
||||
color={{ _light: 'gray.300', _dark: 'gray.600' }}
|
||||
w={{ base: '160px', lg: '240px' }}
|
||||
h={{ base: '123px', lg: '184px' }}
|
||||
/>
|
||||
<Heading
|
||||
fontSize={{ base: '18px', lg: '32px' }}
|
||||
lineHeight={{ base: '24px', lg: '40px' }}
|
||||
h={{ base: '24px', lg: '40px' }}
|
||||
level="1"
|
||||
mt={{ base: 3, lg: 6 }}
|
||||
>
|
||||
Block countdown
|
||||
@@ -41,7 +44,7 @@ const BlockCountdownIndex = () => {
|
||||
>
|
||||
<FilterInput
|
||||
placeholder="Search by block number"
|
||||
size="xs"
|
||||
size="sm"
|
||||
type="number"
|
||||
name="search_term"
|
||||
/>
|
||||
|
||||
+8
-6
@@ -1,4 +1,4 @@
|
||||
import { Tag, Box, Flex, Image } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
@@ -11,14 +11,16 @@ import { getPoolTitle } from 'lib/pools/getPoolTitle';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import * as addressStubs from 'stubs/address';
|
||||
import { POOL } from 'stubs/pools';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { Tag } from 'toolkit/chakra/tag';
|
||||
import PoolInfo from 'ui/pool/PoolInfo';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import * as PoolEntity from 'ui/shared/entities/pool/PoolEntity';
|
||||
import InfoButton from 'ui/shared/InfoButton';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import VerifyWith from 'ui/shared/VerifyWith';
|
||||
|
||||
@@ -71,10 +73,10 @@ const Pool = () => {
|
||||
return externalLinks
|
||||
.map((link) => {
|
||||
return (
|
||||
<LinkExternal h="34px" key={ link.url } href={ link.url } alignItems="center" display="inline-flex" minW="120px">
|
||||
<Link external h="34px" key={ link.url } href={ link.url } alignItems="center" display="inline-flex" minW="120px">
|
||||
<Image boxSize={ 5 } mr={ 2 } src={ link.image } alt={ `${ link.title } icon` }/>
|
||||
{ link.title }
|
||||
</LinkExternal>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
}, [ externalLinks ]);
|
||||
@@ -125,7 +127,7 @@ const Pool = () => {
|
||||
size="lg"
|
||||
/>
|
||||
) : null }
|
||||
contentAfter={ <Skeleton isLoaded={ !isPlaceholderData }><Tag>Pool</Tag></Skeleton> }
|
||||
contentAfter={ <Skeleton loading={ isPlaceholderData }><Tag>Pool</Tag></Skeleton> }
|
||||
secondRow={ titleSecondRow }
|
||||
isLoading={ isPlaceholderData }
|
||||
withTextAd
|
||||
|
||||
+12
-11
@@ -1,4 +1,4 @@
|
||||
import { Show, Hide, Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
@@ -39,7 +39,7 @@ const Pools = () => {
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<Show below="lg" ssr={ false }>
|
||||
<Box hideFrom="lg">
|
||||
{ poolsQuery.data?.items.map((item, index) => (
|
||||
<PoolsListItem
|
||||
key={ item.contract_address + (poolsQuery.isPlaceholderData ? index : '') }
|
||||
@@ -47,22 +47,22 @@ const Pools = () => {
|
||||
item={ item }
|
||||
/>
|
||||
)) }
|
||||
</Show>
|
||||
<Hide below="lg" ssr={ false }>
|
||||
</Box>
|
||||
<Box hideBelow="lg">
|
||||
<PoolsTable
|
||||
items={ poolsQuery.data?.items ?? [] }
|
||||
top={ poolsQuery.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
|
||||
isLoading={ poolsQuery.isPlaceholderData }
|
||||
page={ poolsQuery.pagination.page }
|
||||
/>
|
||||
</Hide>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
const filter = (
|
||||
<FilterInput
|
||||
w={{ base: '100%', lg: '360px' }}
|
||||
size="xs"
|
||||
size="sm"
|
||||
onChange={ handleSearchTermChange }
|
||||
placeholder="Pair, token symbol or token address"
|
||||
initialValue={ searchTerm }
|
||||
@@ -78,9 +78,9 @@ const Pools = () => {
|
||||
mt={ -6 }
|
||||
display={{ base: poolsQuery.pagination.isVisible ? 'flex' : 'none', lg: 'flex' }}
|
||||
>
|
||||
<Hide below="lg">
|
||||
<Box hideBelow="lg">
|
||||
{ filter }
|
||||
</Hide>
|
||||
</Box>
|
||||
<Pagination { ...poolsQuery.pagination } ml="auto"/>
|
||||
</ActionBar>
|
||||
</>
|
||||
@@ -94,15 +94,16 @@ const Pools = () => {
|
||||
/>
|
||||
<DataListDisplay
|
||||
isError={ poolsQuery.isError }
|
||||
items={ poolsQuery.data?.items }
|
||||
itemsNum={ poolsQuery.data?.items.length }
|
||||
emptyText="There are no pools."
|
||||
content={ content }
|
||||
actionBar={ actionBar }
|
||||
filterProps={{
|
||||
emptyFilteredText: `Couldn${ apos }t find pools that matches your filter query.`,
|
||||
hasActiveFilters: Boolean(debouncedSearchTerm),
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{ content }
|
||||
</DataListDisplay>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+10
-8
@@ -1,4 +1,5 @@
|
||||
import { Flex, Box, Tooltip, useClipboard, useColorModeValue } from '@chakra-ui/react';
|
||||
import type { HTMLChakraProps } from '@chakra-ui/react';
|
||||
import { Flex, Box } from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
|
||||
@@ -6,6 +7,8 @@ import type { StaticRoute } from 'nextjs-routes';
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import useFetch from 'lib/hooks/useFetch';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import useClipboard from 'toolkit/hooks/useClipboard';
|
||||
import ContentLoader from 'ui/shared/ContentLoader';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import EmptySearchResult from 'ui/shared/EmptySearchResult';
|
||||
@@ -21,8 +24,8 @@ interface IconInfo {
|
||||
fileSize: number;
|
||||
}
|
||||
|
||||
const Item = ({ name, fileSize, bgColor }: IconInfo & { bgColor: string }) => {
|
||||
const { hasCopied, onCopy } = useClipboard(name, 1000);
|
||||
const Item = ({ name, fileSize, bgColor }: IconInfo & HTMLChakraProps<'div'>) => {
|
||||
const { hasCopied, copy } = useClipboard(name, 1000);
|
||||
const [ copied, setCopied ] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -41,11 +44,11 @@ const Item = ({ name, fileSize, bgColor }: IconInfo & { bgColor: string }) => {
|
||||
wordBreak="break-word"
|
||||
maxW="100px"
|
||||
textAlign="center"
|
||||
onClick={ onCopy }
|
||||
onClick={ copy }
|
||||
cursor="pointer"
|
||||
>
|
||||
<IconSvg name={ name as IconName } boxSize="100px" bgColor={ bgColor } borderRadius="base"/>
|
||||
<Tooltip label={ copied ? 'Copied' : 'Copy to clipboard' } isOpen={ copied }>
|
||||
<Tooltip content={ copied ? 'Copied' : 'Copy to clipboard' } open={ copied }>
|
||||
<Box fontWeight={ 500 } mt={ 2 }>{ name }</Box>
|
||||
</Tooltip>
|
||||
<Box color="text_secondary">{ formatFileSize(fileSize) }</Box>
|
||||
@@ -55,7 +58,6 @@ const Item = ({ name, fileSize, bgColor }: IconInfo & { bgColor: string }) => {
|
||||
|
||||
const Sprite = () => {
|
||||
const [ searchTerm, setSearchTerm ] = React.useState('');
|
||||
const bgColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.100');
|
||||
|
||||
const fetch = useFetch();
|
||||
const { data, isFetching, isError } = useQuery({
|
||||
@@ -83,7 +85,7 @@ const Sprite = () => {
|
||||
|
||||
return (
|
||||
<Flex flexWrap="wrap" fontSize="sm" columnGap={ 5 } rowGap={ 5 } justifyContent="flex-start">
|
||||
{ items.map((item) => <Item key={ item.name } { ...item } bgColor={ bgColor }/>) }
|
||||
{ items.map((item) => <Item key={ item.name } { ...item } bgColor={{ _light: 'blackAlpha.100', _dark: 'whiteAlpha.100' }}/>) }
|
||||
</Flex>
|
||||
);
|
||||
})();
|
||||
@@ -99,7 +101,7 @@ const Sprite = () => {
|
||||
}, { num: 0, fileSize: 0 });
|
||||
}, [ data ]);
|
||||
|
||||
const searchInput = <FilterInput placeholder="Search by name..." onChange={ setSearchTerm } isLoading={ isFetching } minW={{ base: '100%', lg: '300px' }}/>;
|
||||
const searchInput = <FilterInput placeholder="Search by name..." onChange={ setSearchTerm } loading={ isFetching } minW={{ base: '100%', lg: '300px' }}/>;
|
||||
const totalEl = total ? <Box ml="auto">Items: { total.num } / Size: { formatFileSize(total.fileSize) }</Box> : null;
|
||||
|
||||
const contentAfter = (
|
||||
|
||||
+15
-15
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
|
||||
import type { Pool } from 'types/api/pools';
|
||||
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
|
||||
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
||||
@@ -64,33 +64,33 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
hint="Fully Diluted Valuation: theoretical market cap if all tokens were in circulation"
|
||||
>
|
||||
Base token FDV
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.base_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
<DetailedInfo.ItemLabel
|
||||
isLoading={ isPlaceholderData }
|
||||
hint="Current market capitalization of the base token"
|
||||
>
|
||||
Base token market cap
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.base_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
<DetailedInfo.ItemLabel
|
||||
isLoading={ isPlaceholderData }
|
||||
hint="Fully Diluted Valuation: theoretical market cap if all tokens were in circulation"
|
||||
>
|
||||
Quote token FDV
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.quote_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
@@ -102,7 +102,7 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
Quote token market cap
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.quote_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
@@ -114,7 +114,7 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
Liquidity
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.liquidity).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
@@ -126,7 +126,7 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
DEX
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
{ data.dex.name }
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Pool } from 'types/api/pools';
|
||||
|
||||
import getPoolLinks from 'lib/pools/getPoolLinks';
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import PoolEntity from 'ui/shared/entities/pool/PoolEntity';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
|
||||
|
||||
type Props = {
|
||||
@@ -32,19 +32,19 @@ const UserOpsListItem = ({ item, isLoading }: Props) => {
|
||||
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Liquidity</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Skeleton loading={ isLoading }>
|
||||
${ Number(item.liquidity).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</ListItemMobileGrid.Value>
|
||||
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>View in</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Skeleton loading={ isLoading }>
|
||||
{ externalLinks.map((link) => (
|
||||
<LinkExternal href={ link.url } key={ link.url } display="inline-flex">
|
||||
<Link external href={ link.url } key={ link.url } display="inline-flex">
|
||||
<Image src={ link.image } alt={ link.title } boxSize={ 5 } mr={ 2 }/>
|
||||
{ link.title }
|
||||
</LinkExternal>
|
||||
</Link>
|
||||
)) }
|
||||
</Skeleton>
|
||||
</ListItemMobileGrid.Value>
|
||||
|
||||
+13
-14
@@ -1,10 +1,9 @@
|
||||
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Pool } from 'types/api/pools';
|
||||
|
||||
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
|
||||
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
|
||||
import { default as Thead } from 'ui/shared/TheadSticky';
|
||||
|
||||
import PoolsTableItem from './PoolsTableItem';
|
||||
|
||||
@@ -17,21 +16,21 @@ type Props = {
|
||||
|
||||
const PoolsTable = ({ items, page, isLoading, top }: Props) => {
|
||||
return (
|
||||
<Table minWidth="900px">
|
||||
<Thead top={ top ?? ACTION_BAR_HEIGHT_DESKTOP }>
|
||||
<Tr>
|
||||
<Th width="70%">Pool</Th>
|
||||
<Th width="30%">DEX </Th>
|
||||
<Th width="130px" isNumeric>Liquidity</Th>
|
||||
<Th width="75px" isNumeric>View in</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<TableRoot minWidth="900px">
|
||||
<TableHeaderSticky top={ top ?? ACTION_BAR_HEIGHT_DESKTOP }>
|
||||
<TableRow>
|
||||
<TableColumnHeader width="70%">Pool</TableColumnHeader>
|
||||
<TableColumnHeader width="30%">DEX </TableColumnHeader>
|
||||
<TableColumnHeader width="130px" isNumeric>Liquidity</TableColumnHeader>
|
||||
<TableColumnHeader width="75px" isNumeric>View in</TableColumnHeader>
|
||||
</TableRow>
|
||||
</TableHeaderSticky>
|
||||
<TableBody>
|
||||
{ items.map((item, index) => (
|
||||
<PoolsTableItem key={ item.contract_address + (isLoading ? index : '') } item={ item } index={ index } page={ page } isLoading={ isLoading }/>
|
||||
)) }
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableBody>
|
||||
</TableRoot>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+23
-20
@@ -1,14 +1,17 @@
|
||||
import { Flex, Box, Td, Tr, Text, Image, Tooltip } from '@chakra-ui/react';
|
||||
import { Flex, Box, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Pool } from 'types/api/pools';
|
||||
|
||||
import getItemIndex from 'lib/getItemIndex';
|
||||
import getPoolLinks from 'lib/pools/getPoolLinks';
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { TableCell, TableRow } from 'toolkit/chakra/table';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import PoolEntity from 'ui/shared/entities/pool/PoolEntity';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
|
||||
type Props = {
|
||||
item: Pool;
|
||||
@@ -26,10 +29,10 @@ const PoolsTableItem = ({
|
||||
const externalLinks = getPoolLinks(item);
|
||||
|
||||
return (
|
||||
<Tr>
|
||||
<Td>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Flex gap={ 2 } alignItems="start">
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Skeleton loading={ isLoading }>
|
||||
<Text px={ 2 }>{ getItemIndex(index, page) }</Text>
|
||||
</Skeleton>
|
||||
<Box overflow="hidden">
|
||||
@@ -42,29 +45,29 @@ const PoolsTableItem = ({
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>
|
||||
<Skeleton isLoaded={ !isLoading }>{ item.dex.name }</Skeleton>
|
||||
</Td>
|
||||
<Td isNumeric>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton loading={ isLoading }>{ item.dex.name }</Skeleton>
|
||||
</TableCell>
|
||||
<TableCell isNumeric>
|
||||
<Skeleton loading={ isLoading }>
|
||||
${ Number(item.liquidity).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td isNumeric>
|
||||
<Skeleton isLoaded={ !isLoading } display="flex" gap={ 2 } justifyContent="center">
|
||||
</TableCell>
|
||||
<TableCell isNumeric>
|
||||
<Skeleton loading={ isLoading } display="flex" gap={ 2 } justifyContent="center">
|
||||
{ externalLinks.map((link) => (
|
||||
<Tooltip label={ link.title } key={ link.url }>
|
||||
<Tooltip content={ link.title } key={ link.url }>
|
||||
<Box display="inline-block">
|
||||
<LinkExternal href={ link.url } display="inline-flex">
|
||||
<Link external href={ link.url } display="inline-flex">
|
||||
<Image src={ link.image } alt={ link.title } boxSize={ 5 }/>
|
||||
</LinkExternal>
|
||||
</Link>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
)) }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
</Tr>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -63,13 +63,12 @@ export interface EntityProps extends EntityBase.EntityBaseProps {
|
||||
|
||||
const BlobEntity = (props: EntityProps) => {
|
||||
const partsProps = distributeEntityProps(props);
|
||||
const content = <Content { ...partsProps.content }/>;
|
||||
|
||||
return (
|
||||
<Container { ...partsProps.container }>
|
||||
<Icon { ...partsProps.icon }/>
|
||||
<Link { ...partsProps.link }>
|
||||
<Content { ...partsProps.content }/>
|
||||
</Link>
|
||||
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
|
||||
<Copy { ...partsProps.copy }/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -107,13 +107,12 @@ export interface EntityProps extends EntityBase.EntityBaseProps {
|
||||
|
||||
const PoolEntity = (props: EntityProps) => {
|
||||
const partsProps = distributeEntityProps(props);
|
||||
const content = <Content { ...partsProps.content }/>;
|
||||
|
||||
return (
|
||||
<Container w="100%" { ...partsProps.container }>
|
||||
<Icon { ...partsProps.icon }/>
|
||||
<Link { ...partsProps.link }>
|
||||
<Content { ...partsProps.content }/>
|
||||
</Link>
|
||||
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
+40
-40
@@ -1,15 +1,10 @@
|
||||
import {
|
||||
PopoverTrigger,
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
Flex,
|
||||
Link,
|
||||
Image,
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
import Popover from 'ui/shared/chakra/Popover';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
|
||||
const externalTxFeature = config.features.externalTxs;
|
||||
@@ -23,38 +18,43 @@ const TxExternalTxs: React.FC<Props> = ({ data }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = (
|
||||
<Box textStyle="sm">
|
||||
<Flex alignItems="center" gap={ 2 } fontSize="md" mb={ 3 }>
|
||||
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } width={ 5 } height={ 5 }/>
|
||||
{ externalTxFeature.chainName } transaction{ data.length > 1 ? 's' : '' }
|
||||
</Flex>
|
||||
<Flex flexDirection="column" gap={ 2 } w="100%" maxHeight="460px" overflowY="auto">
|
||||
{ data.map((txHash) => (
|
||||
<TxEntity
|
||||
key={ txHash }
|
||||
hash={ txHash }
|
||||
href={ externalTxFeature.explorerUrlTemplate.replace('{hash}', txHash) }
|
||||
isExternal
|
||||
/>
|
||||
)) }
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover placement="bottom-end" openDelay={ 300 } isLazy trigger="hover">
|
||||
<PopoverTrigger>
|
||||
<Link
|
||||
_hover={{ textDecoration: 'none', color: 'link_hovered' }}
|
||||
display="inline-flex"
|
||||
alignItems="center"
|
||||
gap={ 2 }
|
||||
>
|
||||
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } width={ 5 } height={ 5 }/>
|
||||
{ data.length } { externalTxFeature.chainName } txn{ data.length > 1 ? 's' : '' }
|
||||
</Link>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent border="1px solid" borderColor="divider" w={{ base: '300px', lg: '460px' }}>
|
||||
<PopoverBody fontWeight={ 400 } fontSize="sm">
|
||||
<Flex alignItems="center" gap={ 2 } fontSize="md" mb={ 3 }>
|
||||
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } width={ 5 } height={ 5 }/>
|
||||
{ externalTxFeature.chainName } transaction{ data.length > 1 ? 's' : '' }
|
||||
</Flex>
|
||||
<Flex flexDirection="column" gap={ 2 } w="100%" maxHeight="460px" overflowY="auto">
|
||||
{ data.map((txHash) => (
|
||||
<TxEntity
|
||||
key={ txHash }
|
||||
hash={ txHash }
|
||||
href={ externalTxFeature.explorerUrlTemplate.replace('{hash}', txHash) }
|
||||
isExternal
|
||||
/>
|
||||
)) }
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Tooltip
|
||||
content={ content }
|
||||
variant="popover"
|
||||
positioning={{ placement: 'bottom-end' }}
|
||||
openDelay={ 300 }
|
||||
contentProps={{ w: { base: '300px', lg: '460px' } }}
|
||||
>
|
||||
<Link
|
||||
_hover={{ textDecoration: 'none', color: 'link.primary.hover' }}
|
||||
display="inline-flex"
|
||||
alignItems="center"
|
||||
gap={ 2 }
|
||||
>
|
||||
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } boxSize={ 5 }/>
|
||||
{ data.length } { externalTxFeature.chainName } txn{ data.length > 1 ? 's' : '' }
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user