Commit e52386b8 authored by tom's avatar tom

blobs, pools and block countdown pages

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