Commit 3ac5cf01 authored by tom's avatar tom

display social links and website

parent bcd6ad2b
...@@ -44,6 +44,7 @@ import type { ...@@ -44,6 +44,7 @@ import type {
TokenInventoryResponse, TokenInventoryResponse,
TokenInstance, TokenInstance,
TokenInstanceTransfersCount, TokenInstanceTransfersCount,
TokenVerifiedInfo,
} from 'types/api/token'; } from 'types/api/token';
import type { TokensResponse, TokensFilters, TokenInstanceTransferResponse } from 'types/api/tokens'; import type { TokensResponse, TokensFilters, TokenInstanceTransferResponse } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer'; import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
...@@ -322,6 +323,12 @@ export const RESOURCES = { ...@@ -322,6 +323,12 @@ export const RESOURCES = {
path: '/api/v2/tokens/:hash', path: '/api/v2/tokens/:hash',
pathParams: [ 'hash' as const ], pathParams: [ 'hash' as const ],
}, },
token_verified_info: {
path: '/api/v1/chains/:chainId/token-infos/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ],
endpoint: appConfig.contractInfoApi.endpoint,
basePath: appConfig.contractInfoApi.basePath,
},
token_counters: { token_counters: {
path: '/api/v2/tokens/:hash/counters', path: '/api/v2/tokens/:hash/counters',
pathParams: [ 'hash' as const ], pathParams: [ 'hash' as const ],
...@@ -559,6 +566,7 @@ Q extends 'address_coin_balance_chart' ? AddressCoinBalanceHistoryChart : ...@@ -559,6 +566,7 @@ Q extends 'address_coin_balance_chart' ? AddressCoinBalanceHistoryChart :
Q extends 'address_logs' ? LogsResponseAddress : Q extends 'address_logs' ? LogsResponseAddress :
Q extends 'address_tokens' ? AddressTokensResponse : Q extends 'address_tokens' ? AddressTokensResponse :
Q extends 'token' ? TokenInfo : Q extends 'token' ? TokenInfo :
Q extends 'token_verified_info' ? TokenVerifiedInfo :
Q extends 'token_counters' ? TokenCounters : Q extends 'token_counters' ? TokenCounters :
Q extends 'token_transfers' ? TokenTransferResponse : Q extends 'token_transfers' ? TokenTransferResponse :
Q extends 'token_holders' ? TokenHolders : Q extends 'token_holders' ? TokenHolders :
......
import type { TokenInfoApplication } from './account';
import type { AddressParam } from './addressParams'; import type { AddressParam } from './addressParams';
export type TokenType = 'ERC-20' | 'ERC-721' | 'ERC-1155'; export type TokenType = 'ERC-20' | 'ERC-721' | 'ERC-1155';
...@@ -59,3 +60,5 @@ export interface TokenInventoryResponse { ...@@ -59,3 +60,5 @@ export interface TokenInventoryResponse {
export type TokenInventoryPagination = { export type TokenInventoryPagination = {
unique_token: number; unique_token: number;
} }
export type TokenVerifiedInfo = Omit<TokenInfoApplication, 'id' | 'status'>;
...@@ -4,6 +4,7 @@ import React, { useEffect } from 'react'; ...@@ -4,6 +4,7 @@ import React, { useEffect } from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import appConfig from 'configs/app/config';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
...@@ -90,6 +91,12 @@ const TokenPageContent = () => { ...@@ -90,6 +91,12 @@ const TokenPageContent = () => {
queryOptions: { enabled: Boolean(router.query.hash) }, queryOptions: { enabled: Boolean(router.query.hash) },
}); });
const isVerifiedInfoEnabled = Boolean(appConfig.contractInfoApi.endpoint);
const verifiedInfoQuery = useApiQuery('token_verified_info', {
pathParams: { hash: hashString, chainId: appConfig.network.id },
queryOptions: { enabled: Boolean(tokenQuery.data) && isVerifiedInfoEnabled },
});
const contractTabs = useContractTabs(contractQuery.data); const contractTabs = useContractTabs(contractQuery.data);
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
...@@ -187,7 +194,7 @@ const TokenPageContent = () => { ...@@ -187,7 +194,7 @@ const TokenPageContent = () => {
</> </>
) } ) }
<TokenContractInfo tokenQuery={ tokenQuery }/> <TokenContractInfo tokenQuery={ tokenQuery }/>
<TokenDetails tokenQuery={ tokenQuery }/> <TokenDetails tokenQuery={ tokenQuery } verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/>
{ /* should stay before tabs to scroll up with pagination */ } { /* should stay before tabs to scroll up with pagination */ }
<Box ref={ scrollRef }></Box> <Box ref={ scrollRef }></Box>
......
...@@ -4,7 +4,7 @@ import { useRouter } from 'next/router'; ...@@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { scroller } from 'react-scroll'; import { scroller } from 'react-scroll';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo, TokenVerifiedInfo } from 'types/api/token';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
...@@ -14,11 +14,15 @@ import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; ...@@ -14,11 +14,15 @@ import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow'; import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
import TokenDetailsVerifiedInfo from './TokenDetails/TokenDetailsVerifiedInfo';
interface Props { interface Props {
tokenQuery: UseQueryResult<TokenInfo>; tokenQuery: UseQueryResult<TokenInfo>;
verifiedInfoQuery: UseQueryResult<TokenVerifiedInfo>;
isVerifiedInfoEnabled: boolean;
} }
const TokenDetails = ({ tokenQuery }: Props) => { const TokenDetails = ({ tokenQuery, verifiedInfoQuery, isVerifiedInfoEnabled }: Props) => {
const router = useRouter(); const router = useRouter();
const tokenCountersQuery = useApiQuery('token_counters', { const tokenCountersQuery = useApiQuery('token_counters', {
...@@ -56,7 +60,7 @@ const TokenDetails = ({ tokenQuery }: Props) => { ...@@ -56,7 +60,7 @@ const TokenDetails = ({ tokenQuery }: Props) => {
throw Error('Token fetch error', { cause: tokenQuery.error as unknown as Error }); throw Error('Token fetch error', { cause: tokenQuery.error as unknown as Error });
} }
if (tokenQuery.isLoading) { if (tokenQuery.isLoading || (isVerifiedInfoEnabled && verifiedInfoQuery.isLoading)) {
return ( return (
<Grid mt={ 10 } columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px"> <Grid mt={ 10 } columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
<DetailsSkeletonRow w="10%"/> <DetailsSkeletonRow w="10%"/>
...@@ -95,6 +99,14 @@ const TokenDetails = ({ tokenQuery }: Props) => { ...@@ -95,6 +99,14 @@ const TokenDetails = ({ tokenQuery }: Props) => {
rowGap={{ base: 1, lg: 3 }} rowGap={{ base: 1, lg: 3 }}
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden" templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
> >
{ verifiedInfoQuery.data && (
<DetailsInfoItem
title="Links"
hint="Links"
>
<TokenDetailsVerifiedInfo data={ verifiedInfoQuery.data }/>
</DetailsInfoItem>
) }
{ exchangeRate && ( { exchangeRate && (
<DetailsInfoItem <DetailsInfoItem
title="Price" title="Price"
......
import { Flex, Link, Icon } from '@chakra-ui/react';
import React from 'react';
import type { TokenVerifiedInfo } from 'types/api/token';
import githubIcon from 'icons/social/git.svg';
import placeholderIcon from 'icons/social/stats.svg';
import telegramIcon from 'icons/social/telega.svg';
import twitterIcon from 'icons/social/tweet.svg';
import LinkExternal from 'ui/shared/LinkExternal';
interface Props {
data: TokenVerifiedInfo;
}
interface SocialLink {
field: keyof TokenVerifiedInfo;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
hint: string;
}
const SOCIAL_LINKS: Array<SocialLink> = [
{ field: 'github', icon: githubIcon, hint: 'Github account' },
{ field: 'twitter', icon: twitterIcon, hint: 'Twitter account' },
{ field: 'telegram', icon: telegramIcon, hint: 'Telegram account' },
{ field: 'openSea', icon: placeholderIcon, hint: 'OpenSea page' },
{ field: 'linkedin', icon: placeholderIcon, hint: 'LinkedIn page' },
{ field: 'facebook', icon: placeholderIcon, hint: 'Facebook account' },
{ field: 'discord', icon: placeholderIcon, hint: 'Discord account' },
{ field: 'medium', icon: placeholderIcon, hint: 'Medium account' },
{ field: 'slack', icon: placeholderIcon, hint: 'Slack account' },
{ field: 'reddit', icon: placeholderIcon, hint: 'Reddit account' },
];
const TokenDetailsVerifiedInfo = ({ data }: Props) => {
const websiteName = (() => {
try {
const url = new URL(data.projectWebsite);
return url.host;
} catch (error) { }
})();
const socialLinks = SOCIAL_LINKS
.map((link) => ({ ...link, href: data[link.field] }))
.filter(({ href }) => href);
return (
<Flex alignItems={{ base: 'flex-start', lg: 'center' }} flexDir={{ base: 'column', lg: 'row' }} rowGap={ 2 } columnGap={ 6 }>
{ websiteName && <LinkExternal href={ data.projectWebsite }>{ websiteName }</LinkExternal> }
{ socialLinks.length > 0 && (
<Flex columnGap={ 2 }>
{ socialLinks.map((link) => (
<Link href={ link.href } key={ link.field } variant="secondary" boxSize={ 5 } aria-label={ link.hint } title={ link.hint } target="_blank">
<Icon as={ link.icon } boxSize={ 5 }/>
</Link>
)) }
</Flex>
) }
</Flex>
);
};
export default React.memo(TokenDetailsVerifiedInfo);
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