Skip to content

Commit

Permalink
nice display tests won't pass
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaut committed Nov 28, 2024
1 parent 4f62f9e commit c87dfd3
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"graphql": "^16.9.0",
"graphql-request": "^7.1.2",
"graphql-ws": "^5.16.0",
"json5": "^2.2.3",
"polkadot-api": "^1.7.7",
"react": "18.3.1",
"react-dom": "18.3.1",
Expand Down
168 changes: 162 additions & 6 deletions packages/ui/src/components/CallInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { ReactNode, useMemo } from 'react'
import { ApiType, useApi } from '../contexts/ApiContext'
import { getExtrinsicName } from '../utils/getExtrinsicName'
import { isProxyCall } from '../utils/isProxyCall'
// import { formatBigIntBalance } from '../utils/formatBnBalance'
// import MultisigCompactDisplay from './MultisigCompactDisplay'
import { formatBigIntBalance } from '../utils/formatBnBalance'
import { HiOutlineArrowTopRightOnSquare as LaunchIcon } from 'react-icons/hi2'
import { Link } from './library'
import { usePjsLinks } from '../hooks/usePjsLinks'
import { Alert } from '@mui/material'
// import { isTypeBalanceWithBalanceCall, isTypeAccount } from '../utils'
import { CallDataInfoFromChain } from '../hooks/usePendingTx'
import { JSONprint } from '../utils/jsonPrint'
import { Transaction } from 'polkadot-api'
import MultisigCompactDisplay from './MultisigCompactDisplay'
import { ChainInfoHuman } from '../contexts/PeopleChainApiContext'

interface Props {
aggregatedData: Omit<CallDataInfoFromChain, 'from' | 'timestamp'>
Expand All @@ -30,13 +30,150 @@ interface CreateTreeParams {
unit: string
name?: string
api: ApiType
chainInfo?: ChainInfoHuman
}

const createUlTree = ({ name, decodedCall }: CreateTreeParams) => {
const isWhiteListedCall = (type: string, value: string) => {
return [
'Balances.transfer',
'Balances.transfer_keep_alive',
'Balances.transfer_all',
'Balances.transfer_allow_death',
'Bounties.award_bounty',
'ChildBounties.award_child_bounty',
'ChildBounties.add_child_bounty',
'ChildBounties.propose_curator',
'Staking.bond',
'Staking.unbond',
'Staking.bond_extra',
'Staking.rebond',
'Staking.nominate',
'Proxy.add_proxy',
'Proxy.remove_proxy',
'Proxy.kill_pure'
].includes(`${type}.${value}`)
}

const isBatchedCall = (type: string, value: string) => {
return ['Utility.batch', 'Utility.batch_all', 'Utility.force_batch'].includes(`${type}.${value}`)
}

const formatBalance = (amount: bigint, label: string, chainInfo: ChainInfoHuman) => (
<li>
{label}:{' '}
{formatBigIntBalance(amount, chainInfo?.tokenDecimals, {
tokenSymbol: chainInfo?.tokenSymbol
})}
</li>
)

const eachFieldRendered = (value: Record<string, any>, chainInfo: ChainInfoHuman) => {
// for transfer, nomination, staking, bounties
const bigIntKey = ['value', 'fee', 'max_additional'].find((key) => typeof value[key] === 'bigint')

if (bigIntKey) {
return formatBalance(value[bigIntKey], bigIntKey, chainInfo)
}

// for Staking.nominate
if (Array.isArray(value.targets) && value.targets.length > 0) {
return (
<li>
targets:{' '}
<ul>
{value.targets.map((target: any) => {
const address = target.type === 'Id' ? target.value : null

if (address) {
return (
<li key={address}>
<MultisigCompactDisplay address={address} />
</li>
)
}

return <li key={address}>{JSONprint(target)}</li>
})}
</ul>
</li>
)
}

// for Staking.bond
if (value.payee?.type === 'Account') {
return (
<li>
payee: <MultisigCompactDisplay address={value.payee?.value} />
</li>
)
}

// that's an Account with MultiAddress.Id
const multiAddressKey = ['dest', 'beneficiary', 'curator', 'delegate', 'spawner'].find(
(key) => typeof value[key] === 'object' && value[key].type === 'Id'
)

if (multiAddressKey) {
return (
<li>
{multiAddressKey}: <MultisigCompactDisplay address={value[multiAddressKey]?.value} />
</li>
)
}

return <li>{JSONprint(value)} </li>
}

const preparedCall = (
decodedCall: CreateTreeParams['decodedCall'],
chainInfo: ChainInfoHuman,
isBatch = false
) => {
if (!decodedCall) return

if (isBatchedCall(decodedCall.type, decodedCall.value.type)) {
const lowerLevelCalls = decodedCall.value.value.calls as Array<Record<string, any>>

return lowerLevelCalls.map((call, index) => {
return (
<BatchCallStyled key={`${call.type}-${index}`}>
{preparedCall(call as CreateTreeParams['decodedCall'], chainInfo, true)}
</BatchCallStyled>
)
})
}

if (isWhiteListedCall(decodedCall.type, decodedCall.value.type)) {
const lowerLevelCall = decodedCall.value.value
if (typeof lowerLevelCall === 'object') {
return (
<>
{isBatch && (
<ExtrinsicNameStyled>
{getExtrinsicName(decodedCall.type, decodedCall.value.type)}
</ExtrinsicNameStyled>
)}
<ul>
{Object.entries(lowerLevelCall).map(([key, value]) =>
eachFieldRendered({ [key]: value }, chainInfo)
)}
</ul>
</>
)
} else {
return <PreStyled>{JSONprint(decodedCall)}</PreStyled>
}
}

return <PreStyled>{JSONprint(decodedCall)}</PreStyled>
}

const createUlTree = ({ name, decodedCall, chainInfo }: CreateTreeParams) => {
if (!decodedCall) return
if (!name) return
if (!chainInfo) return

return <PreStyled>{JSONprint(decodedCall.value.value)}</PreStyled>
return preparedCall(decodedCall, chainInfo)
}

const filterProxyProxy = (agg: Props['aggregatedData']): Props['aggregatedData'] => {
Expand Down Expand Up @@ -112,7 +249,7 @@ const CallInfo = ({
<Expander
expanded={expanded}
title="Params"
content={createUlTree({ name, decodedCall, decimals, unit, api })}
content={createUlTree({ name, decodedCall, decimals, unit, api, chainInfo })}
/>
)}
{children}
Expand Down Expand Up @@ -141,6 +278,25 @@ const PreStyled = styled('pre')`
overflow: auto;
`

const BatchCallStyled = styled('div')`
border-radius: ${({ theme }) => theme.custom.borderRadius};
border: 1px solid ${({ theme }) => theme.custom.gray[400]};
margin-top: 1rem;
margin-left: 1rem;
padding: 1rem;
padding-top: 0;
`

const ExtrinsicNameStyled = styled('span')`
font-weight: 500;
background-color: ${({ theme }) => theme.palette.primary.main};
color: white;
margin-left: -1rem;
border-top-left-radius: 0.75rem;
padding: 0.2rem 0.4rem 0.2rem 0.4rem;
display: inline-block;
`

export default styled(CallInfo)`
flex: 1;
overflow: hidden;
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/src/components/Transactions/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const Transaction = ({
}: Props) => {
const { onOpenSigningModal } = useModals()
const isProxy = useMemo(() => isProxyCall(aggregatedData.name), [aggregatedData])
// FIXME this is duplicated
const appliedClass = useMemo(() => (isProxy ? 'blue' : 'red'), [isProxy])

const onOpenModal = useCallback(() => {
Expand Down Expand Up @@ -70,7 +69,7 @@ const Transaction = ({
<TransactionCallInfoBoxStyled>
<CallInfo
withLink
aggregatedData={aggregatedData}
aggregatedData={{ ...aggregatedData }}
>
{(isProposer || possibleSigners.length > 0) && (
<TransactionFooterStyled>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/hooks/useCallInfoFromCallData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export const useCallInfoFromCallData = (callData?: HexString) => {
call: tx,
hash: hashFromTx(callData),
weight: { proof_size: weight.proof_size, ref_time: weight.ref_time },
method: tx?.decodedCall.type,
section: tx?.decodedCall.value.type
section: tx?.decodedCall.type,
method: tx?.decodedCall.value.type
})
setIsGettingCallInfo(false)
})
Expand Down
2 changes: 0 additions & 2 deletions packages/ui/src/utils/isTypeAccount.ts

This file was deleted.

7 changes: 0 additions & 7 deletions packages/ui/src/utils/isTypeBalance.ts

This file was deleted.

16 changes: 11 additions & 5 deletions packages/ui/src/utils/jsonPrint.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Binary } from 'polkadot-api'
import json5 from 'json5'

export const JSONprint = (e: unknown) =>
JSON.stringify(
e,
(_, v) => (typeof v === 'bigint' ? v.toString() : v instanceof Binary ? v.asText() : v),
4
)
json5
.stringify(e, {
replacer: (_, v) =>
typeof v === 'bigint' ? v.toString() : v instanceof Binary ? v.asText() : v,
space: 4
})
// remove { and }
.slice(1, -1)
// remove trailing comma if any
.replace(/,\s*$/, '')
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10685,6 +10685,7 @@ __metadata:
graphql: ^16.9.0
graphql-request: ^7.1.2
graphql-ws: ^5.16.0
json5: ^2.2.3
polkadot-api: ^1.7.7
react: 18.3.1
react-dom: 18.3.1
Expand Down

0 comments on commit c87dfd3

Please sign in to comment.