Commit 0d75bac8 authored by tom's avatar tom

network explorers popover

parent e0d2882e
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/ethereum/goerli/transaction','address':'/ethereum/ethereum/goerli/address'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address'}}]
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token'}}]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
# network config
......
......@@ -15,6 +15,7 @@ export interface NetworkExplorer {
paths: {
tx?: string;
address?: string;
token?: string;
};
}
......
......@@ -14,6 +14,7 @@ import { useAppContext } from 'lib/appContext';
import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
......@@ -45,7 +46,7 @@ const TokenPageContent = () => {
const scrollRef = React.useRef<HTMLDivElement>(null);
const hashString = router.query.hash?.toString();
const hashString = getQueryParamString(router.query.hash);
const queryClient = useQueryClient();
......@@ -252,7 +253,7 @@ const TokenPageContent = () => {
</>
) }
<TokenContractInfo tokenQuery={ tokenQuery }/>
<TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/>
<TokenVerifiedInfo hash={ hashString } verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/>
<TokenDetails tokenQuery={ tokenQuery }/>
{ /* should stay before tabs to scroll up with pagination */ }
<Box ref={ scrollRef }></Box>
......
......@@ -6,10 +6,9 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
import networkExplorers from 'lib/networks/networkExplorers';
import getQueryParamString from 'lib/router/getQueryParamString';
import TextAd from 'ui/shared/ad/TextAd';
import LinkExternal from 'ui/shared/LinkExternal';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
......@@ -40,27 +39,10 @@ const TransactionPageContent = () => {
queryOptions: { enabled: Boolean(hash) },
});
const explorersLinks = networkExplorers
.filter((explorer) => explorer.paths.tx)
.map((explorer) => {
const url = new URL(explorer.paths.tx + '/' + hash, explorer.baseUrl);
return <LinkExternal key={ explorer.baseUrl } href={ url.toString() }>Open in { explorer.title }</LinkExternal>;
});
const additionals = (
<Flex justifyContent="space-between" alignItems="center" flexGrow={ 1 } flexWrap="wrap">
{ data?.tx_tag && <Tag my={ 2 }>{ data.tx_tag }</Tag> }
{ explorersLinks.length > 0 && (
<Flex
alignItems="center"
flexWrap="wrap"
columnGap={ 6 }
rowGap={ 3 }
ml={{ base: 'initial', lg: 'auto' }}
>
{ explorersLinks }
</Flex>
) }
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 'initial', lg: 'auto' }}/>
</Flex>
);
......
import { Flex, Button, Icon, chakra, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import arrowIcon from 'icons/arrows/east-mini.svg';
import searchIcon from 'icons/search.svg';
import networkExplorers from 'lib/networks/networkExplorers';
import LinkExternal from 'ui/shared/LinkExternal';
interface Props {
className?: string;
type: keyof TNetworkExplorer['paths'];
pathParam: string;
}
const NetworkExplorers = ({ className, type, pathParam }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const explorersLinks = networkExplorers
.filter((explorer) => explorer.paths[type])
.map((explorer) => {
const url = new URL(explorer.paths[type] + '/' + pathParam, explorer.baseUrl);
return <LinkExternal key={ explorer.baseUrl } href={ url.toString() }>{ explorer.title }</LinkExternal>;
});
if (explorersLinks.length === 0) {
return null;
}
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<Button
className={ className }
size="sm"
variant="outline"
colorScheme="gray"
onClick={ onToggle }
aria-label="Verify in other explorers"
fontWeight={ 500 }
px={ 2 }
h="30px"
>
<Icon as={ searchIcon } boxSize={ 4 } mr={ 1 }/>
<span>Explorers</span>
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 1 }/>
</Button>
</PopoverTrigger>
<PopoverContent w="240px">
<PopoverBody >
<chakra.span color="text_secondary" fontSize="xs">Verify with other explorers</chakra.span>
<Flex
alignItems="center"
flexWrap="wrap"
columnGap={ 6 }
rowGap={ 3 }
mt={ 3 }
>
{ explorersLinks }
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default React.memo(chakra(NetworkExplorers));
import { Flex, Skeleton } from '@chakra-ui/react';
import { Flex, Skeleton, useColorModeValue } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react';
import type { TokenVerifiedInfo as TTokenVerifiedInfo } from 'types/api/token';
import LinkExternal from 'ui/shared/LinkExternal';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
interface Props {
verifiedInfoQuery: UseQueryResult<TTokenVerifiedInfo>;
isVerifiedInfoEnabled: boolean;
hash: string;
}
const TokenVerifiedInfo = ({ verifiedInfoQuery, isVerifiedInfoEnabled }: Props) => {
const TokenVerifiedInfo = ({ verifiedInfoQuery, isVerifiedInfoEnabled, hash }: Props) => {
const { data, isLoading, isError } = verifiedInfoQuery;
const websiteLinkBg = useColorModeValue('gray.100', 'gray.700');
const content = (() => {
const explorers = <NetworkExplorers type="token" pathParam={ hash }/>;
if (!isVerifiedInfoEnabled) {
return <span>explorers</span>;
return explorers;
}
if (isLoading) {
......@@ -38,7 +43,7 @@ const TokenVerifiedInfo = ({ verifiedInfoQuery, isVerifiedInfoEnabled }: Props)
try {
const url = new URL(data.projectWebsite);
return (
<LinkExternal href={ data.projectWebsite } px="10px" py="5px" bgColor="gray.100" borderRadius="base">{ url.host }</LinkExternal>
<LinkExternal href={ data.projectWebsite } px="10px" py="5px" bgColor={ websiteLinkBg } borderRadius="base">{ url.host }</LinkExternal>
);
} catch (error) {
return null;
......@@ -49,12 +54,12 @@ const TokenVerifiedInfo = ({ verifiedInfoQuery, isVerifiedInfoEnabled }: Props)
<>
{ websiteLink }
<Skeleton w="130px" h="30px" borderRadius="base"/>
<Skeleton w="120px" h="30px" borderRadius="base"/>
{ explorers }
</>
);
})();
return <Flex columnGap={ 3 } mt={ 5 }>{ content }</Flex>;
return <Flex columnGap={ 3 } rowGap={ 3 } mt={ 5 } flexWrap="wrap" _empty={{ display: 'none' }}>{ content }</Flex>;
};
export default React.memo(TokenVerifiedInfo);
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