Skip to content

Commit

Permalink
Add Tokens page and dashboard component
Browse files Browse the repository at this point in the history
  • Loading branch information
csillag committed Jun 16, 2023
1 parent dfca051 commit 38a833c
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .changelog/546.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add token overview page and dashboard component

23 changes: 23 additions & 0 deletions src/app/components/Tokens/TokenLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FC } from 'react'
import { Link as RouterLink } from 'react-router-dom'
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 isTablet ? (
<TrimLinkLabel label={name || address} to={RouteUtils.getTokenRoute(scope, address)} />
) : (
<Link component={RouterLink} to={RouteUtils.getTokenRoute(scope, address)}>
{name || address}
</Link>
)
}
82 changes: 82 additions & 0 deletions src/app/components/Tokens/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useTranslation } from 'react-i18next'
import { EvmToken } from '../../../oasis-indexer/api'
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
import { TablePaginationProps } from '../Table/TablePagination'
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, pagination, limit } = props
const { t } = useTranslation()
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}
/>
)
}
55 changes: 55 additions & 0 deletions src/app/pages/DashboardPage/TopTokens.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import { Link as RouterLink } from 'react-router-dom'
import Link from '@mui/material/Link'
import { Layer, useGetRuntimeEvmTokens } from '../../../oasis-indexer/api'
import { NUMBER_OF_ITEMS_ON_DASHBOARD } from '../../config'
import { COLORS } from '../../../styles/theme/colors'
import { AppErrors } from '../../../types/errors'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { RouteUtils } from '../../utils/route-utils'
import { Tokens } from '../../components/Tokens'

const limit = NUMBER_OF_ITEMS_ON_DASHBOARD

export const TopTokens: FC = () => {
const { t } = useTranslation()
const scope = useRequiredScopeParam()
const { network, layer } = scope
if (layer === Layer.consensus) {
throw AppErrors.UnsupportedLayer
// Listing the latest consensus transactions is not yet supported.
// We should use useGetConsensusTransactions()
}
const tokensQuery = useGetRuntimeEvmTokens(network, layer, { limit })

return (
<Card>
<CardHeader
disableTypography
component="h3"
title={t('tokens.title')}
action={
<Link
component={RouterLink}
to={RouteUtils.getTopTokensRoute(scope)}
sx={{ color: COLORS.brandExtraDark }}
>
{t('common.viewAll')}
</Link>
}
/>
<CardContent>
<Tokens
tokens={tokensQuery.data?.data.evm_tokens}
isLoading={tokensQuery.isLoading}
limit={limit}
pagination={false}
/>
</CardContent>
</Card>
)
}
2 changes: 2 additions & 0 deletions src/app/pages/DashboardPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TransactionsStats } from './TransactionsStats'
import { TotalTransactions } from './TotalTransactions'
import { PageLayout } from '../../components/PageLayout'
import { ParaTimeSnapshot } from './ParaTimeSnapshot'
import { TopTokens } from './TopTokens'

export const DashboardPage: FC = () => {
const { isMobile } = useScreenSize()
Expand All @@ -19,6 +20,7 @@ export const DashboardPage: FC = () => {
<ParaTimeSnapshot />
<Divider variant="layout" sx={{ mt: isMobile ? 4 : 0 }} />
<LatestTransactions />
<TopTokens />
<Grid container spacing={4}>
<Grid item xs={12} md={6} sx={{ display: 'flex', order: isMobile ? 1 : 0 }}>
<LearningMaterials />
Expand Down
125 changes: 125 additions & 0 deletions src/app/pages/TokenkDetailPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { FC } from 'react'
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>
// )
}
Loading

0 comments on commit 38a833c

Please sign in to comment.