-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add token overview page |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { FC } from 'react' | ||
import { Link as RouterLink } from 'react-router-dom' | ||
import Typography from '@mui/material/Typography' | ||
import Link from '@mui/material/Link' | ||
|
||
import { RouteUtils } from '../../utils/route-utils' | ||
import { TrimLinkLabel } from '../TrimLinkLabel' | ||
import { SearchScope } from '../../../types/searchScope' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
|
||
export const TokenLink: FC<{ scope: SearchScope; address: string; name: string | undefined }> = ({ | ||
scope, | ||
address, | ||
name, | ||
}) => { | ||
const { isTablet } = useScreenSize() | ||
return ( | ||
<Typography variant="mono"> | ||
{isTablet ? ( | ||
<TrimLinkLabel label={name || address} to={RouteUtils.getTokenRoute(scope, address)} /> | ||
) : ( | ||
<Link component={RouterLink} to={RouteUtils.getTokenRoute(scope, address)}> | ||
{name || address} | ||
</Link> | ||
)} | ||
</Typography> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { useTranslation } from 'react-i18next' | ||
import { EvmToken } from '../../../oasis-indexer/api' | ||
import { Table, TableCellAlign, TableColProps } from '../../components/Table' | ||
import { TablePaginationProps } from '../Table/TablePagination' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
import { AccountLink } from '../Account/AccountLink' | ||
import { TokenLink } from './TokenLink' | ||
import { CopyToClipboard } from '../CopyToClipboard' | ||
|
||
type TokensProps = { | ||
tokens?: EvmToken[] | ||
isLoading: boolean | ||
limit: number | ||
verbose?: boolean | ||
pagination: false | TablePaginationProps | ||
} | ||
|
||
export const Tokens = (props: TokensProps) => { | ||
const { isLoading, tokens, verbose, pagination, limit } = props | ||
const { t } = useTranslation() | ||
const { isLaptop } = useScreenSize() | ||
const tableColumns: TableColProps[] = [ | ||
{ content: '' }, | ||
{ content: t('common.name') }, | ||
{ content: t('common.smartContract') }, | ||
{ | ||
content: t('tokens.holdersCount'), | ||
align: TableCellAlign.Right, | ||
}, | ||
{ content: t('tokens.supply'), align: TableCellAlign.Right }, | ||
{ content: t('common.ticker'), align: TableCellAlign.Right }, | ||
] | ||
|
||
const tableRows = tokens?.map((token, index) => { | ||
return { | ||
key: token.contract_addr, | ||
data: [ | ||
{ | ||
content: (index + 1).toLocaleString(), | ||
key: 'index', | ||
}, | ||
{ | ||
content: <TokenLink scope={token} address={token.evm_contract_addr} name={token.name} />, | ||
key: 'name', | ||
}, | ||
{ | ||
content: ( | ||
<span> | ||
<AccountLink scope={token} address={token.evm_contract_addr} /> | ||
<CopyToClipboard value={token.evm_contract_addr} /> | ||
</span> | ||
), | ||
key: 'contactAddress', | ||
}, | ||
{ | ||
content: token.num_holders.toLocaleString(), | ||
key: 'holdersCount', | ||
align: TableCellAlign.Right, | ||
}, | ||
{ | ||
content: token.total_supply, | ||
key: 'supply', | ||
align: TableCellAlign.Right, | ||
}, | ||
{ | ||
content: token.symbol, | ||
key: 'ticker', | ||
align: TableCellAlign.Right, | ||
}, | ||
], | ||
} | ||
}) | ||
|
||
return ( | ||
<Table | ||
columns={tableColumns} | ||
rows={tableRows} | ||
rowsNumber={limit} | ||
name={t('tokens.title')} | ||
isLoading={isLoading} | ||
pagination={pagination} | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { FC } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
import { EvmToken } from '../../../oasis-indexer/api' | ||
import { TextSkeleton } from '../../components/Skeleton' | ||
|
||
// export const TokenDetailPage: FC = () => { | ||
// const { t } = useTranslation() | ||
// const scope = useRequiredScopeParam() | ||
// if (scope.layer === Layer.consensus) { | ||
// throw AppErrors.UnsupportedLayer | ||
// // We should use useGetConsensusBlocksHeight() | ||
// } | ||
// const blockHeight = parseInt(useParams().blockHeight!, 10) | ||
// const { isLoading, data } = useGetRuntimeBlockByHeight( | ||
// scope.network, | ||
// scope.layer, // This is OK, since consensus is already handled separately | ||
// blockHeight, | ||
// ) | ||
// if (!data && !isLoading) { | ||
// throw AppErrors.NotFoundBlockHeight | ||
// } | ||
// const block = data?.data | ||
// | ||
// return ( | ||
// <PageLayout> | ||
// <SubPageCard featured title={t('common.block')}> | ||
// <BlockDetailView isLoading={isLoading} block={block} /> | ||
// </SubPageCard> | ||
// {!!block?.num_transactions && <TransactionsCard scope={scope} blockHeight={blockHeight} />} | ||
// </PageLayout> | ||
// ) | ||
// } | ||
|
||
export const TokenDetailView: FC<{ | ||
isLoading?: boolean | ||
token: EvmToken | undefined | ||
showLayer?: boolean | ||
standalone?: boolean | ||
}> = ({ isLoading, token, showLayer, standalone = false }) => { | ||
const { t } = useTranslation() | ||
const { isMobile } = useScreenSize() | ||
|
||
if (isLoading) return <TextSkeleton numberOfRows={7} /> | ||
if (!token) return null | ||
|
||
return <div>Token details</div> | ||
// const transactionsAnchor = `${RouteUtils.getBlockRoute(block, block.round)}#${transactionsContainerId}` | ||
// const transactionLabel = block.num_transactions.toLocaleString() | ||
// const blockGasLimit = paraTimesConfig[block.layer]?.mainnet.blockGasLimit | ||
// if (!blockGasLimit) throw new Error('blockGasLimit is not configured') | ||
// return ( | ||
// <StyledDescriptionList | ||
// titleWidth={isMobile ? '100px' : '200px'} | ||
// standalone={standalone} | ||
// highlight={block.markAsNew} | ||
// > | ||
// {showLayer && ( | ||
// <> | ||
// <dt>{t('common.paratime')}</dt> | ||
// <dd> | ||
// <DashboardLink scope={block} /> | ||
// </dd> | ||
// </> | ||
// )} | ||
// <dt>{t('common.height')}</dt> | ||
// <dd> | ||
// <BlockLink scope={block} height={block.round} /> | ||
// <CopyToClipboard value={block.round.toString()} /> | ||
// </dd> | ||
// | ||
// <dt>{t('common.hash')}</dt> | ||
// <dd> | ||
// <BlockHashLink scope={block} hash={block.hash} height={block.round} /> | ||
// <CopyToClipboard value={block.hash.toString()} /> | ||
// </dd> | ||
// | ||
// <dt>{t('common.timestamp')}</dt> | ||
// <dd>{formattedTime}</dd> | ||
// | ||
// <dt>{t('common.size')}</dt> | ||
// <dd> | ||
// {t('common.bytes', { | ||
// value: block.size, | ||
// formatParams: { | ||
// value: { | ||
// style: 'unit', | ||
// unit: 'byte', | ||
// unitDisplay: 'long', | ||
// } satisfies Intl.NumberFormatOptions, | ||
// }, | ||
// })} | ||
// </dd> | ||
// | ||
// <dt>{t('common.transactions')}</dt> | ||
// <dd> | ||
// {block.num_transactions ? ( | ||
// <Link href={transactionsAnchor}>{transactionLabel}</Link> | ||
// ) : ( | ||
// transactionLabel | ||
// )} | ||
// </dd> | ||
// | ||
// <dt>{t('common.gasUsed')}</dt> | ||
// <dd> | ||
// {t('block.gasUsed', { | ||
// value: block.gas_used, | ||
// percentage: block.gas_used / blockGasLimit, | ||
// formatParams: { | ||
// percentage: { | ||
// style: 'percent', | ||
// maximumFractionDigits: 2, | ||
// } satisfies Intl.NumberFormatOptions, | ||
// }, | ||
// })} | ||
// </dd> | ||
// | ||
// <dt>{t('common.gasLimit')}</dt> | ||
// <dd>{blockGasLimit.toLocaleString()}</dd> | ||
// </StyledDescriptionList> | ||
// ) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { FC, useEffect, useState } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import Divider from '@mui/material/Divider' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
import { styled } from '@mui/material/styles' | ||
import { PageLayout } from '../../components/PageLayout' | ||
import { SubPageCard } from '../../components/SubPageCard' | ||
import { Layer, useGetRuntimeEvmTokens } from '../../../oasis-indexer/api' | ||
import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE, REFETCH_INTERVAL } from '../../config' | ||
import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' | ||
import Box from '@mui/material/Box' | ||
import { COLORS } from '../../../styles/theme/colors' | ||
import { AppErrors } from '../../../types/errors' | ||
import { TableLayout, TableLayoutButton } from '../../components/TableLayoutButton' | ||
import { LoadMoreButton } from '../../components/LoadMoreButton' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { Tokens } from '../../components/Tokens' | ||
import { TokenDetailView } from '../TokenkDetailPage' | ||
|
||
const PAGE_SIZE = NUMBER_OF_ITEMS_ON_SEPARATE_PAGE | ||
|
||
const TokenDetails = styled(Box)(({ theme }) => ({ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: `0 ${theme.spacing(2)}`, | ||
backgroundColor: COLORS.brandDark, | ||
})) | ||
|
||
export const TokensPage: FC = () => { | ||
const [tableView, setTableView] = useState<TableLayout>(TableLayout.Horizontal) | ||
const { isMobile } = useScreenSize() | ||
const { t } = useTranslation() | ||
const pagination = useSearchParamsPagination('page') | ||
const offset = (pagination.selectedPage - 1) * PAGE_SIZE | ||
const scope = useRequiredScopeParam() | ||
// Consensus is not yet enabled in ENABLED_LAYERS, just some preparation | ||
if (scope.layer === Layer.consensus) { | ||
throw AppErrors.UnsupportedLayer | ||
// Listing the latest consensus blocks is not yet implemented. | ||
// we should call useGetConsensusBlocks() | ||
} | ||
|
||
useEffect(() => { | ||
if (!isMobile) { | ||
setTableView(TableLayout.Horizontal) | ||
} | ||
}, [isMobile, setTableView]) | ||
|
||
const tokensQuery = useGetRuntimeEvmTokens( | ||
scope.network, | ||
scope.layer, // This is OK, since consensus is already handled separately | ||
{ | ||
limit: tableView === TableLayout.Vertical ? offset + PAGE_SIZE : PAGE_SIZE, | ||
offset: tableView === TableLayout.Vertical ? 0 : offset, | ||
}, | ||
{ | ||
query: { | ||
refetchInterval: REFETCH_INTERVAL, | ||
// Keep showing data while loading more | ||
keepPreviousData: tableView === TableLayout.Vertical, | ||
}, | ||
}, | ||
) | ||
|
||
return ( | ||
<PageLayout | ||
mobileFooterAction={ | ||
tableView === TableLayout.Vertical && ( | ||
<LoadMoreButton pagination={pagination} isLoading={tokensQuery.isLoading} /> | ||
) | ||
} | ||
> | ||
{!isMobile && <Divider variant="layout" />} | ||
<SubPageCard | ||
title={t('tokens.title')} | ||
action={isMobile && <TableLayoutButton tableView={tableView} setTableView={setTableView} />} | ||
noPadding={tableView === TableLayout.Vertical} | ||
> | ||
{tableView === TableLayout.Horizontal && ( | ||
<Tokens | ||
isLoading={tokensQuery.isLoading} | ||
tokens={tokensQuery.data?.data.evm_tokens} | ||
limit={PAGE_SIZE} | ||
verbose={true} | ||
pagination={{ | ||
selectedPage: pagination.selectedPage, | ||
linkToPage: pagination.linkToPage, | ||
totalCount: tokensQuery.data?.data.total_count, | ||
isTotalCountClipped: tokensQuery.data?.data.is_total_count_clipped, | ||
rowsPerPage: NUMBER_OF_ITEMS_ON_SEPARATE_PAGE, | ||
}} | ||
/> | ||
)} | ||
{tableView === TableLayout.Vertical && ( | ||
<TokenDetails> | ||
{tokensQuery.isLoading && | ||
[...Array(PAGE_SIZE).keys()].map(key => ( | ||
<TokenDetailView key={key} isLoading={true} token={undefined} standalone /> | ||
))} | ||
|
||
{!tokensQuery.isLoading && | ||
tokensQuery.data?.data.evm_tokens.map(token => ( | ||
<TokenDetailView key={token.contract_addr} token={token} standalone /> | ||
))} | ||
</TokenDetails> | ||
)} | ||
</SubPageCard> | ||
</PageLayout> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters