Skip to content

Commit

Permalink
Feat: change the pagination to cursor and add sorter---contract list (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
alexsupa597 authored Mar 9, 2023
1 parent b4b16ad commit 9a13743
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 44 deletions.
201 changes: 157 additions & 44 deletions pages/contracts.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { GetStaticProps } from 'next'
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
Expand All @@ -10,21 +11,74 @@ import { gql } from 'graphql-request'
import PageTitle from 'components/PageTitle'
import SubpageHead from 'components/SubpageHead'
import Address from 'components/TruncatedAddress'
import Pagination from 'components/Pagination'
import Pagination from 'components/SimplePagination'
import Table from 'components/Table'
import Amount from 'components/Amount'
import SortIcon from 'assets/icons/sort.svg'
import { SIZES } from 'components/PageSize'
import { fetchContractList, PCKB_UDT_INFO, client, GraphQLSchema } from 'utils'
import {
PCKB_UDT_INFO,
client,
GraphQLSchema,
handleSorterArrayAboutPath,
handleSorterArrayInOrder,
sorterType,
} from 'utils'
import styles from './index.module.scss'

interface ContractListProps {
interface Variables {
before: string | null
after: string | null
limit: number
sorter: SmartContractsSorterInput[] | []
}
type SmartContractsSorterInput = {
sort_type: 'ASC' | 'DESC'
sort_value: 'EX_TX_COUNT' | 'ID' | 'NAME' | 'CKB_BALANCE'
}
enum SortTypesEnum {
tx_count_sort = 'tx_count_sort',
balance_sort = 'balance_sort',
}
enum SmartContractsSorterValueEnum {
tx_count_sort = 'EX_TX_COUNT',
balance_sort = 'CKB_BALANCE',
}
type ContractListProps = {
smart_contracts: {
entries: Array<{
name: string
id: string
ckb_balance: string
compiler_file_format: string
compiler_version: string
deployment_tx_hash: string
other_info: string | null
account: {
eth_address: string
transaction_count: number
}
}>
metadata: GraphQLSchema.PageMetadata
}
}

const contractListQuery = gql`
query {
smart_contracts(input: {}) {
query ($before: String, $after: String, $limit: Int, $sorter: SmartContractsSorterInput) {
smart_contracts(input: { before: $before, after: $after, limit: $limit, sorter: $sorter }) {
entries {
name
id
ckb_balance
compiler_file_format
compiler_version
deployment_tx_hash
other_info
account {
eth_address
transaction_count
}
}
metadata {
total_count
after
Expand All @@ -34,11 +88,12 @@ const contractListQuery = gql`
}
`

const fetchList = () =>
const fetchList = (variables: Variables): Promise<ContractListProps['smart_contracts']> =>
client
.request<ContractListProps>(contractListQuery)
.request<ContractListProps>(contractListQuery, variables)
.then(data => data.smart_contracts)
.catch((): ContractListProps['smart_contracts'] => ({
entries: [],
metadata: {
total_count: 0,
before: null,
Expand All @@ -47,21 +102,69 @@ const fetchList = () =>
}))

const ContractList = () => {
const theme = useTheme()
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
const [t] = useTranslation(['list', 'common'])
const title = t('contract_list_title')

const {
query: { page = '1', page_size = SIZES[2] },
push,
asPath,
query: { before = null, after = null, page_size = SIZES[1], tx_count_sort, balance_sort, ...restQuery },
} = useRouter()
const theme = useTheme()
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))

const title = t('contract_list_title')
const sorters = ['tx_count_sort', 'balance_sort']

const DEFAULT_SORTERS: sorterType[] = [
{ type: 'tx_count_sort', order: 'ASC' },
{ type: 'balance_sort', order: 'ASC' },
]

const sorterArrayFromPath = handleSorterArrayAboutPath(asPath, sorters)

// get a sorter array to query listdata from server
const sorterArrayForQuery = handleSorterArrayAboutPath(asPath, sorters, SmartContractsSorterValueEnum)

const handleUrlForPush = (clickedSorter: sorterType = null) => {
const searchParams = new URLSearchParams({
...restQuery,
page_size: page_size as string,
})

const orderedSorter = handleSorterArrayInOrder(clickedSorter ? sorterArrayFromPath : DEFAULT_SORTERS, clickedSorter)
for (const item of orderedSorter) {
searchParams.append(item.type, item.order)
}

return `${asPath.split('?')[0] ?? ''}?${searchParams}`
}

useEffect(() => {
if (!sorterArrayFromPath.length) {
push(handleUrlForPush())
}
}, [sorterArrayFromPath])

const { isLoading, data } = useQuery(
['contracts', page, page_size],
() => fetchContractList({ page: page as string, page_size: page_size as string }),
{ refetchInterval: 10000 },
['contract-list', before, after, page_size, tx_count_sort, balance_sort],
() =>
fetchList({
before: before as string,
after: after as string,
limit: Number.isNaN(+page_size) ? +SIZES[1] : +page_size,
sorter: sorterArrayForQuery,
}),
{
refetchInterval: 10000,
},
)

const { data: list } = useQuery(['contract-list'], () => fetchList())
const handleSorterClick = (e: React.MouseEvent<HTMLOrSVGElement>, type) => {
const {
dataset: { order },
} = e.currentTarget
push(handleUrlForPush({ type, order: order === 'DESC' ? 'ASC' : 'DESC' }))
}

return (
<>
Expand Down Expand Up @@ -99,7 +202,7 @@ const ContractList = () => {
{!isLoading ? (
<Typography variant="inherit" color="secondary" fontWeight={500} fontSize={{ xs: 14, md: 16 }}>
{t(`n_kinds_in_total`, {
number: list?.metadata.total_count ?? '-',
number: data?.metadata.total_count ?? '-',
})}
</Typography>
) : (
Expand All @@ -118,8 +221,20 @@ const ContractList = () => {
<th>
{t(`balance`)}
<span style={{ textTransform: 'none' }}>{`(${PCKB_UDT_INFO.symbol})`}</span>
<SortIcon
onClick={e => handleSorterClick(e, SortTypesEnum.balance_sort)}
data-order={balance_sort}
className={styles.sorter}
/>
</th>
<th style={{ textAlign: 'end' }}>
{t(`tx_count`)}
<SortIcon
onClick={e => handleSorterClick(e, SortTypesEnum.tx_count_sort)}
data-order={tx_count_sort}
className={styles.sorter}
/>
</th>
<th style={{ textAlign: 'end' }}>{t(`tx_count`)}</th>
</tr>
</thead>
<tbody>
Expand All @@ -131,22 +246,27 @@ const ContractList = () => {
</td>
</tr>
))
) : data.meta.totalPage ? (
data.contracts.map(c => (
<tr key={c.id} title={c.address}>
) : data.metadata.total_count ? (
data.entries.map(c => (
<tr key={c.id} title={c.account.eth_address}>
<td style={{ width: '25%' }}>
<Address address={c.address} leading={isMobile ? 8 : 30} sx={{ width: 'min-content' }} />
<Address
address={c.account.eth_address}
leading={isMobile ? 8 : 30}
sx={{ width: 'min-content' }}
/>
</td>
<td title={c.name}>{c.name}</td>
<td style={{ textTransform: 'capitalize' }} title={c.compiler.fileFormat}>
{c.compiler.fileFormat?.split(' ')[0] ?? '-'}
<td style={{ textTransform: 'capitalize' }} title={c?.compiler_file_format}>
{c?.compiler_file_format?.split(' ')[0] ?? '-'}
</td>
<td title={c.compiler.version}>{c.compiler.version?.split('+')[0]}</td>
<td title={c.balance}>
<Amount amount={c.balance ?? '0'} udt={{ symbol: PCKB_UDT_INFO.symbol, decimal: 10 }} />
<td title={c.compiler_version}>{c.compiler_version?.split('+')[0]}</td>
<td title={c.ckb_balance}>
<Amount amount={c.ckb_balance ?? '0'} udt={{ symbol: PCKB_UDT_INFO.symbol, decimal: 10 }} />
</td>
<td style={{ textAlign: 'end' }} title={`${c.txCount}`}>
{c.txCount.toLocaleString('en')}
<td style={{ textAlign: 'end' }} title={`${c.account.transaction_count}`}>
{/* {c.account.transaction_count.toLocaleString('en')} */}
{c.account.transaction_count}
</td>
</tr>
))
Expand All @@ -160,22 +280,15 @@ const ContractList = () => {
</tbody>
</Table>
</TableContainer>

<Stack
direction="row"
flexWrap="wrap"
justifyContent={isMobile ? 'center' : 'end'}
alignItems="center"
mt={{ xs: 0, md: 2 }}
px={{ xs: 1.5, md: 3 }}
>
{/* <PageSize pageSize={+page_size} /> */}
{isLoading ? (
<Skeleton animation="wave" width="20px" />
) : (
<Pagination total={data?.meta.totalPage * +page_size} page={+page} pageSize={+page_size} />
)}
</Stack>
{data?.metadata.total_count ? (
<div style={{ overflow: 'hidden' }}>
{!data ? (
<Skeleton animation="wave" width="calc(100% - 48px)" sx={{ mx: '24px', my: '20px' }} />
) : (
<Pagination {...data.metadata} />
)}
</div>
) : null}
</Box>
</Container>
</>
Expand Down
9 changes: 9 additions & 0 deletions pages/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,12 @@
}
}
}

.sorter {
cursor: pointer;
margin-left: 4px;

&[data-order='DESC'] {
transform: rotate(0.5turn);
}
}
50 changes: 50 additions & 0 deletions utils/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,53 @@ export const handleCopy = async (value: string) => {
export const handleNftImageLoadError = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
e.currentTarget.src = '/images/nft-placeholder.svg'
}

// return a sorter array
export const handleSorterArrayAboutPath = (url: string, sorters: string[], sorterValueEnum = null) => {
const params = url.slice(url.indexOf('?') + 1)
const sorterParamsArray = []

const searchParams = new URLSearchParams(params)
const keys = [...searchParams.keys()]

keys?.map((item, index) => {
if (sorters.includes(item)) {
// return sort array which used for query, like: [{sort_type: ASC , sort_value: xxx}]
if (sorterValueEnum) {
sorterParamsArray.push({
sort_type: decodeURIComponent([...searchParams.values()][index]),
sort_value: sorterValueEnum[item],
})

// return sort array which from url, like: [{type: xxx , order: ASC}]
} else {
sorterParamsArray.push({ type: item, order: decodeURIComponent([...searchParams.values()][index]) })
}
}
})

return sorterParamsArray
}
export type sorterType = {
type: string
order: 'ASC' | 'DESC'
}

export const handleSorterArrayInOrder = (pathSorterArray: sorterType[], onClickedSorter: sorterType = null) => {
if (onClickedSorter) {
const { type } = onClickedSorter

// return a sorter array with the clicked one is on the first position
pathSorterArray.sort((preSorter, curSorter) => {
if (preSorter.type === type) {
return -1
} else if (curSorter.type === type) {
return 1
} else {
return 0
}
})
pathSorterArray[0] = onClickedSorter
}
return pathSorterArray
}

0 comments on commit 9a13743

Please sign in to comment.