diff --git a/packages/page-parachains/src/Overview/Parachain.tsx b/packages/page-parachains/src/Overview/Parachain.tsx index 2bb557d463..53a21a77dd 100644 --- a/packages/page-parachains/src/Overview/Parachain.tsx +++ b/packages/page-parachains/src/Overview/Parachain.tsx @@ -20,7 +20,8 @@ interface Props { className?: string; id: ParaId; isScheduled?: boolean; - lastInclusion?: [string, string]; + lastBacked?: [string, string, BN]; + lastInclusion?: [string, string, BN]; } type QueryResult = [Option, Option, Vec, Vec, Vec, Vec, Option]; @@ -35,7 +36,7 @@ interface QueryState { watermark: BlockNumber | null; } -const transformLast = { +const transformHeader = { transform: (header: Header) => header.number.unwrap() }; @@ -62,12 +63,12 @@ const optionsMulti = { }) }; -function Parachain ({ bestNumber, className = '', id, isScheduled, lastInclusion }: Props): React.ReactElement { +function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, lastInclusion }: Props): React.ReactElement { const { api } = useApi(); const { api: paraApi, endpoints } = useParaApi(id); const paraBest = useCall(paraApi?.derive.chain.bestNumber); const paraIssu = useCall(paraApi?.query.balances?.totalIssuance); - const lastRelayNumber = useCall(lastInclusion && api.rpc.chain.getHeader, [lastInclusion && lastInclusion[1]], transformLast); + const lastRelayNumber = useCall(lastInclusion && api.rpc.chain.getHeader, [lastInclusion && lastInclusion[1]], transformHeader); const paraInfo = useCallMulti([ [api.query.paras.heads, id], [api.query.paras.futureCodeUpgrades, id], @@ -112,6 +113,11 @@ function Parachain ({ bestNumber, className = '', id, isScheduled, lastInclusion : paraInfo.watermark && formatNumber(paraInfo.watermark) } + + {lastBacked && + {formatNumber(lastBacked[2])} + } + {paraBest && <>{formatNumber(paraBest)}} {paraIssu && } diff --git a/packages/page-parachains/src/Overview/ParachainList.tsx b/packages/page-parachains/src/Overview/ParachainList.tsx index 13dde133ee..fb7574f1e8 100644 --- a/packages/page-parachains/src/Overview/ParachainList.tsx +++ b/packages/page-parachains/src/Overview/ParachainList.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignedBlockExtended } from '@polkadot/api-derive/type'; -import type { CandidateReceipt, ParaId } from '@polkadot/types/interfaces'; +import type { CandidateReceipt, Event, ParaId } from '@polkadot/types/interfaces'; import type { ScheduledProposals } from '../types'; import BN from 'bn.js'; @@ -19,48 +19,67 @@ interface Props { scheduled?: ScheduledProposals[]; } +type EventMap = Record; + +interface LastEvents { + lastBacked: EventMap; + lastIncluded: EventMap; +} + +function includeEntry (map: EventMap, event: Event, blockHash: string, blockNumber: BN): void { + const { descriptor } = event.data[0] as CandidateReceipt; + + map[descriptor.paraId.toString()] = [ + blockHash, + descriptor.relayParent.toHex(), + blockNumber + ]; +} + function ParachainList ({ ids, scheduled }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const bestNumber = useCall(api.derive.chain.bestNumber); const lastBlock = useCall(api.derive.chain.subscribeNewBlocks); - const [lastIncluded, setLastIncluded] = useState>({}); + const [{ lastBacked, lastIncluded }, setLastEvents] = useState({ lastBacked: {}, lastIncluded: {} }); const scheduledIds = useMemo( - () => scheduled - ? scheduled.reduce((all: Record, { scheduledIds }: ScheduledProposals): Record => { - return scheduledIds.reduce((all: Record, id) => ({ ...all, [id.toString()]: true }), all); - }, {}) - : {}, + () => (scheduled || []).reduce((all: Record, { scheduledIds }: ScheduledProposals): Record => { + return scheduledIds.reduce((all: Record, id) => ({ ...all, [id.toString()]: true }), all); + }, {}), [scheduled] ); useEffect((): void => { - lastBlock && setLastIncluded((prev) => { - const updated: Record = {}; + lastBlock && setLastEvents((prev) => { + const backed: Record = {}; + const included: Record = {}; + const blockNumber = lastBlock.block.header.number.unwrap(); const blockHash = lastBlock.block.header.hash.toHex(); - let wasUpdated = false; + let wasIncluded = false; + let wasBacked = false; lastBlock.events.forEach(({ event, phase }) => { - if (phase.isApplyExtrinsic && api.events.inclusion.CandidateBacked.is(event)) { - const { descriptor } = event.data[0] as CandidateReceipt; - - updated[descriptor.paraId.toString()] = [ - blockHash, - descriptor.relayParent.toHex() - ]; - wasUpdated = true; + if (phase.isApplyExtrinsic) { + if (api.events.inclusion.CandidateBacked.is(event)) { + includeEntry(backed, event, blockHash, blockNumber); + wasBacked = true; + } else if (api.events.inclusion.CandidateIncluded.is(event)) { + includeEntry(included, event, blockHash, blockNumber); + wasIncluded = true; + } } }); - return wasUpdated - ? Object.entries(prev).reduce((updated: Record, [id, hashes]): Record => { - if (!updated[id]) { - updated[id] = hashes; - } - - return updated; - }, updated) + return wasBacked || wasIncluded + ? { + lastBacked: wasBacked + ? { ...prev.lastBacked, ...backed } + : prev.lastBacked, + lastIncluded: wasIncluded + ? { ...prev.lastIncluded, ...included } + : prev.lastIncluded + } : prev; }); }, [api, lastBlock]); @@ -68,7 +87,8 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { const headerRef = useRef([ [t('parachains'), 'start', 3], [t('heads'), 'start'], - [t('relay parent'), undefined, 2], + [t('included (parent)'), undefined, 2], + [t('backed')], [t('chain best'), 'media--900'], [t('issuance'), 'media--1100'], [t('upgrade'), 'media--1300'], @@ -86,6 +106,7 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { id={id} isScheduled={scheduledIds[id.toString()]} key={id.toString()} + lastBacked={lastBacked[id.toString()]} lastInclusion={lastIncluded[id.toString()]} /> ))}