Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display type in tokens table #685

Merged
merged 5 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/685.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Display type in tokens table
6 changes: 6 additions & 0 deletions src/app/components/Tokens/TokenDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DashboardLink } from '../../pages/ParatimeDashboardPage/DashboardLink'
import { DelayedContractVerificationIcon } from '../ContractVerificationIcon'
import Box from '@mui/material/Box'
import { COLORS } from '../../../styles/theme/colors'
import { TokenTypeTag } from './TokenList'

export const TokenDetails: FC<{
isLoading?: boolean
Expand Down Expand Up @@ -40,6 +41,11 @@ export const TokenDetails: FC<{
<Box sx={{ ml: 3, fontWeight: 700, color: COLORS.grayMedium }}>({token.symbol})</Box>
</dd>

<dt>{t('common.type')}</dt>
<dd>
<TokenTypeTag tokenType={token.type} />
</dd>

<dt>{t(isMobile ? 'common.smartContract_short' : 'common.smartContract')}</dt>
<dd>
<span>
Expand Down
46 changes: 44 additions & 2 deletions src/app/components/Tokens/TokenList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { useTranslation } from 'react-i18next'
import { EvmToken } from '../../../oasis-nexus/api'
import { EvmToken, EvmTokenType } from '../../../oasis-nexus/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'
import { DelayedContractVerificationIcon } from '../ContractVerificationIcon'
import { DelayedContractVerificationIcon, verificationIconBoxHeight } from '../ContractVerificationIcon'
import Box from '@mui/material/Box'
import {
getTokenTypeDescription,
getTokenTypeStrictName,
tokenBackgroundColor,
tokenBorderColor,
} from '../../../types/tokens'
import { FC } from 'react'
import Typography from '@mui/material/Typography'
import { COLORS } from '../../../styles/theme/colors'
import { SxProps } from '@mui/material/styles'

type TokensProps = {
tokens?: EvmToken[]
Expand All @@ -15,12 +25,40 @@ type TokensProps = {
pagination: false | TablePaginationProps
}

export const TokenTypeTag: FC<{ tokenType: EvmTokenType; sx?: SxProps }> = ({ tokenType, sx = {} }) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to TokenPills that wraps around mui's Chip.

We should choose more consistent names for "the background rounded label thing"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about DogTag ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I mean for a common ancestor, with common sizing and fond settings, and then create or descendants from that, with consistent names, either Tag or Pill)

const { t } = useTranslation()
return (
<Box
sx={{
background: tokenBackgroundColor[tokenType],
border: `1px solid ${tokenBorderColor[tokenType]}`,
display: 'inline-block',
borderRadius: 2,
py: 1,
px: 3,
fontSize: 12,
height: verificationIconBoxHeight,
verticalAlign: 'middle',
textAlign: 'center',
...sx,
}}
>
<Typography component="span">{getTokenTypeDescription(t, tokenType)}</Typography>
&nbsp;
<Typography component="span" color={COLORS.grayMedium}>
{t('common.parentheses', { subject: getTokenTypeStrictName(t, tokenType) })}
</Typography>
</Box>
)
}

export const TokenList = (props: TokensProps) => {
const { isLoading, tokens, pagination, limit } = props
const { t } = useTranslation()
const tableColumns: TableColProps[] = [
{ key: 'index', content: '' },
{ key: 'name', content: t('common.name') },
{ key: 'type', content: t('common.type') },
{ key: 'contract', content: t('common.smartContract') },
{ key: 'verification', content: t('contract.verification.title') },
{
Expand Down Expand Up @@ -54,6 +92,10 @@ export const TokenList = (props: TokensProps) => {
),
key: 'name',
},
{
key: 'type',
content: <TokenTypeTag tokenType={token.type} sx={{ width: '100%' }} />,
},
{
content: (
<span>
Expand Down
15 changes: 12 additions & 3 deletions src/app/pages/AccountDetailsPage/AccountTokensCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { useAccount } from './hook'
import { TokenLink } from '../../components/Tokens/TokenLink'
import { AccountLink } from '../../components/Account/AccountLink'
import {
getTokenTypePluralDescription,
getTokenTypePluralName,
getTokenTypeStrictName,
} from '../../../types/tokens'

type AccountTokensCardProps = {
type: EvmTokenType
Expand All @@ -28,8 +33,7 @@ export const AccountTokensCard: FC<AccountTokensCardProps> = ({ type }) => {
const address = useLoaderData() as string
const { t } = useTranslation()
const locationHash = useLocation().hash.replace('#', '')
const tokenLabel = t(`account.${type}` as any)
const tokenListLabel = t('account.tokensListTitle', { token: tokenLabel })
const tokenListLabel = getTokenTypePluralName(t, type)
const tableColumns: TableColProps[] = [
{ key: 'name', content: t('common.name') },
{ key: 'contract', content: t('common.smartContract') },
Expand Down Expand Up @@ -86,7 +90,12 @@ export const AccountTokensCard: FC<AccountTokensCardProps> = ({ type }) => {
<CardHeader disableTypography component="h3" title={tokenListLabel} />
<CardContent>
{!isLoading && !account?.tokenBalances[type]?.length && (
<CardEmptyState label={t('account.emptyTokenList', { token: tokenLabel })} />
<CardEmptyState
label={t('account.emptyTokenList', {
spec: getTokenTypeStrictName(t, type),
description: getTokenTypePluralDescription(t, type),
})}
/>
)}

<Table
Expand Down
5 changes: 3 additions & 2 deletions src/app/pages/AccountDetailsPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CardEmptyState } from './CardEmptyState'
import { contractCodeContainerId } from './ContractCodeCard'
import { useTokenInfo } from '../TokenDashboardPage/hook'
import { accountTokenTransfersContainerId } from './AccountTokenTransfersCard'
import { getTokenTypePluralName } from '../../../types/tokens'

export const AccountDetailsPage: FC = () => {
const { t } = useTranslation()
Expand Down Expand Up @@ -65,12 +66,12 @@ export const AccountDetailsPage: FC = () => {
{ label: t('common.transactions'), to: txLink, visible: showTxs },
{ label: t('tokens.transfers'), to: tokenTransfersLink, visible: showTokenTransfers },
{
label: t('account.tokensListTitle', { token: t(`account.ERC20`) }),
label: getTokenTypePluralName(t, EvmTokenType.ERC20),
to: erc20Link,
visible: showErc20,
},
{
label: t('account.tokensListTitle', { token: t(`account.ERC721`) }),
label: getTokenTypePluralName(t, EvmTokenType.ERC721),
to: erc721Link,
visible: showErc721,
},
Expand Down
6 changes: 4 additions & 2 deletions src/app/pages/TokenDashboardPage/TokenDetailsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import { useTranslation } from 'react-i18next'
import { AccountLink } from '../../components/Account/AccountLink'
import { CopyToClipboard } from '../../components/CopyToClipboard'
import { DelayedContractVerificationIcon } from '../../components/ContractVerificationIcon'
import { getTokenTypeName } from './TokenTypeCard'
import { getNameForTicker, Ticker } from '../../../types/ticker'
import { DelayedContractCreatorInfo } from '../../components/Account/ContractCreatorInfo'
import CardContent from '@mui/material/CardContent'
import { TokenTypeTag } from '../../components/Tokens/TokenList'

export const TokenDetailsCard: FC = () => {
const { t } = useTranslation()
Expand Down Expand Up @@ -57,7 +57,9 @@ export const TokenDetailsCard: FC = () => {
</dd>

<dt>{t('common.type')} </dt>
<dd>{getTokenTypeName(t, token.type)} </dd>
<dd>
<TokenTypeTag tokenType={token.type} />
</dd>

<dt>{t('contract.creator')}</dt>
<dd>
Expand Down
18 changes: 2 additions & 16 deletions src/app/pages/TokenDashboardPage/TokenTypeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,7 @@ import { COLORS } from '../../../styles/theme/colors'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { useTokenInfo } from './hook'
import { useLoaderData } from 'react-router-dom'
import { EvmTokenType } from '../../../oasis-nexus/api'
import { TFunction } from 'i18next'
import { exhaustedTypeWarning } from '../../../types/errors'

export const getTokenTypeName = (t: TFunction, type: EvmTokenType): string => {
switch (type) {
case 'ERC20':
return t('account.ERC20')
case 'ERC721':
return t('account.ERC721')
default:
exhaustedTypeWarning('Unknown token type', type)
return type
}
}
import { getTokenTypeName } from '../../../types/tokens'

export const TokenTypeCard: FC = () => {
const { t } = useTranslation()
Expand All @@ -32,7 +18,7 @@ export const TokenTypeCard: FC = () => {
const { token, isFetched } = useTokenInfo(scope, address)

return (
<SnapshotCard title={t('tokens.type')} withConstantHeight>
<SnapshotCard title={t('common.type')} withConstantHeight>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
{isFetched && (
<>
Expand Down
7 changes: 5 additions & 2 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
"appName": "Oasis Explorer",
"account": {
"cantLoadDetails": "Unfortunately we couldn't load the account details at this time. Please try again later.",
"emptyTokenList": "This account holds no {{token}} tokens.",
"emptyTokenList": "This account holds no {{spec}} {{description}}.",
"emptyTransactionList": "There are no transactions on record for this account.",
"emptyTokenTransferList": "There are no token transfers on record for this account.",
"ERC20": "ERC-20",
"ERC721": "ERC-721",
"noTokens": "This account holds no tokens",
"showMore": "+ {{counter}} more",
"title": "Account",
"tokensListTitle": "{{token}} Tokens",
"transactionsListTitle": "Account Transactions",
"totalReceived": "Total Received",
"totalSent": "Total Sent"
Expand Down Expand Up @@ -65,8 +64,11 @@
"lessThanAmount": "< {{value}} {{ticker}}",
"missing": "n/a",
"name": "Name",
"nft": "NFT",
"nfts": "NFTs",
"oasis": "Oasis",
"paratime": "Paratime",
"parentheses":"({{subject}})",
"percentage": "Percentage",
"rank": "Rank",
"select": "Select",
Expand Down Expand Up @@ -203,6 +205,7 @@
"request": "Request test tokens"
},
"tokens": {
"typeDescription": "{{description}} ({{spec}})",
"emptyTokenHolderList": "There are no token holders on record for this token.",
"holders": "Token Holders",
"holdersValue": "{{ value, number }}",
Expand Down
3 changes: 3 additions & 0 deletions src/styles/theme/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const COLORS = {
brandExtraLight: '#e5e5ef',
brandLight: '#6665d8',
brandMedium: '#0092f6',
brandMedium15: '#d9effe',
brightGray2: '#ececec',
brightGray: '#e6edf3',
ceil: '#8f8cdf',
Expand Down Expand Up @@ -51,4 +52,6 @@ export const COLORS = {
graphLabel: '#191932',
graphLine: '#01F1E3',
paraTimeStatus: '#8081ac',
pink: '#ed32fa',
pink15: '#fce0fe',
} satisfies { [colorName: string]: string }
62 changes: 62 additions & 0 deletions src/types/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { EvmTokenType } from '../oasis-nexus/api'
import { TFunction } from 'i18next'
import { exhaustedTypeWarning } from './errors'
import { COLORS } from '../styles/theme/colors'

export const getTokenTypeDescription = (t: TFunction, tokenType: EvmTokenType): string => {
switch (tokenType) {
case 'ERC20':
return t('common.token')
case 'ERC721':
return t('common.nft')
default:
exhaustedTypeWarning('Unknown token type', tokenType)
return '???'
}
}

export const tokenBackgroundColor: Record<EvmTokenType, string> = {
ERC20: COLORS.brandMedium15,
ERC721: COLORS.pink15,
}

export const tokenBorderColor: Record<EvmTokenType, string> = {
ERC20: COLORS.brandMedium,
ERC721: COLORS.pink,
}

export const getTokenTypePluralDescription = (t: TFunction, tokenType: EvmTokenType): string => {
switch (tokenType) {
case 'ERC20':
return t('common.tokens')
case 'ERC721':
return t('common.nfts')
default:
exhaustedTypeWarning('Unknown token type', tokenType)
return '???'
}
}

export const getTokenTypeStrictName = (t: TFunction, tokenType: EvmTokenType): string => {
switch (tokenType) {
case 'ERC20':
return t('account.ERC20')
case 'ERC721':
return t('account.ERC721')
default:
exhaustedTypeWarning('Unknown token type', tokenType)
return tokenType
}
}

export const getTokenTypeName = (t: TFunction, tokenType: EvmTokenType): string =>
t('tokens.typeDescription', {
spec: getTokenTypeStrictName(t, tokenType),
description: getTokenTypeDescription(t, tokenType),
})

export const getTokenTypePluralName = (t: TFunction, tokenType: EvmTokenType): string =>
t('tokens.typeDescription', {
spec: getTokenTypeStrictName(t, tokenType),
description: getTokenTypePluralDescription(t, tokenType),
})