Rework explorer with events -> extrinsics (#2490)
* Rework explorer with events -> extrinsics * Nonce adjust * Adjust unsigned
This commit is contained in:
parent
fd4ae0ae51
commit
16f1d2de20
@ -7,12 +7,12 @@ import { EventRecord, SignedBlock } from '@polkadot/types/interfaces';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { HeaderExtended } from '@polkadot/api-derive';
|
||||
import { AddressMini, Columar, LinkExternal, Table } from '@polkadot/react-components';
|
||||
import { AddressMini, Columar, Column, LinkExternal, Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Events from './Events';
|
||||
import Events from '../Events';
|
||||
import Extrinsics from './Extrinsics';
|
||||
import Logs from './Logs';
|
||||
|
||||
@ -60,14 +60,25 @@ function BlockByHash ({ className, value }: Props): React.ReactElement<Props> {
|
||||
</Table.Body>
|
||||
</Table>
|
||||
{getBlock && getHeader && (
|
||||
<Columar>
|
||||
<>
|
||||
<Extrinsics
|
||||
blockNumber={blockNumber}
|
||||
events={events}
|
||||
value={getBlock.block.extrinsics}
|
||||
/>
|
||||
<Events value={events} />
|
||||
<Logs value={getHeader.digest.logs} />
|
||||
</Columar>
|
||||
<Columar>
|
||||
<Column>
|
||||
<Events
|
||||
eventClassName='explorer--BlockByHash-block'
|
||||
events={(events || []).filter(({ phase }) => !phase.isApplyExtrinsic)}
|
||||
label={t('system events')}
|
||||
/>
|
||||
</Column>
|
||||
<Column>
|
||||
<Logs value={getHeader.digest.logs} />
|
||||
</Column>
|
||||
</Columar>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/app-explorer authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Column } from '@polkadot/react-components';
|
||||
|
||||
import EventsDisplay from '../Events';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
value?: EventRecord[];
|
||||
}
|
||||
|
||||
function Events ({ value }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!value || !value.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<EventsDisplay
|
||||
eventClassName='explorer--BlockByHash-block'
|
||||
events={
|
||||
value.map((record, index): { key: string; record: EventRecord } => ({
|
||||
key: `${index}`, record
|
||||
}))
|
||||
}
|
||||
label={t('events')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Events);
|
||||
121
packages/page-explorer/src/BlockInfo/Extrinsic.tsx
Normal file
121
packages/page-explorer/src/BlockInfo/Extrinsic.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2017-2020 @polkadot/app-explorer authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BlockNumber, EventRecord, Extrinsic } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { AddressMini, Call, Expander, LinkExternal } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Event from '../Event';
|
||||
|
||||
interface Props {
|
||||
blockNumber?: BlockNumber;
|
||||
className?: string;
|
||||
events?: EventRecord[];
|
||||
index: number;
|
||||
value: Extrinsic;
|
||||
}
|
||||
|
||||
function getEra ({ era }: Extrinsic, blockNumber?: BlockNumber): [number, number] | null {
|
||||
if (blockNumber && era.isMortalEra) {
|
||||
const mortalEra = era.asMortalEra;
|
||||
|
||||
return [mortalEra.birth(blockNumber.toNumber()), mortalEra.death(blockNumber.toNumber())];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function filterEvents (index: number, events: EventRecord[] = []): EventRecord[] {
|
||||
return events.filter(({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index));
|
||||
}
|
||||
|
||||
function ExtrinsicDisplay ({ blockNumber, className, events, index, value }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { meta, method, section } = registry.findMetaCall(value.callIndex);
|
||||
const era = getEra(value, blockNumber);
|
||||
const thisEvents = filterEvents(index, events);
|
||||
|
||||
return (
|
||||
<tr
|
||||
className={className}
|
||||
key={`extrinsic:${index}`}
|
||||
>
|
||||
<td className='top'>
|
||||
<Expander
|
||||
summary={`${section}.${method} (#${formatNumber(index)})`}
|
||||
summaryMeta={meta}
|
||||
>
|
||||
<Call
|
||||
className='details'
|
||||
mortality={
|
||||
era
|
||||
? blockNumber
|
||||
? t('mortal, valid from #{{startAt}} to #{{endsAt}}', {
|
||||
replace: {
|
||||
endsAt: formatNumber(era[1]),
|
||||
startAt: formatNumber(era[0])
|
||||
}
|
||||
})
|
||||
: t('mortal')
|
||||
: t('immortal')
|
||||
}
|
||||
tip={value.tip?.toBn()}
|
||||
value={value}
|
||||
withHash
|
||||
/>
|
||||
</Expander>
|
||||
{value.isSigned
|
||||
? <LinkExternal data={value.hash.toHex()} type='extrinsic' />
|
||||
: null
|
||||
}
|
||||
</td>
|
||||
<td className='top'>
|
||||
{value.isSigned
|
||||
? (
|
||||
<>
|
||||
<AddressMini value={value.signer} />
|
||||
<div className='explorer--BlockByHash-nonce'>
|
||||
{t('index')} {formatNumber(value.nonce)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
: <div className='explorer--BlockByHash-unsigned'>{t('not signed')}</div>
|
||||
}
|
||||
</td>
|
||||
<td className='all top'>
|
||||
{thisEvents.map((event, index) =>
|
||||
<Event
|
||||
className='explorer--BlockByHash-event'
|
||||
key={`event:${index}`}
|
||||
value={event}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(ExtrinsicDisplay)`
|
||||
.explorer--BlockByHash-event+.explorer--BlockByHash-event {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.explorer--BlockByHash-nonce {
|
||||
font-size: 0.75rem;
|
||||
margin-left: 2.25rem;
|
||||
margin-top: -0.5rem;
|
||||
opacity: 0.6;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.explorer--BlockByHash-unsigned {
|
||||
opacity: 0.6;
|
||||
font-weight: 100;
|
||||
}
|
||||
`);
|
||||
@ -2,110 +2,44 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BlockNumber, Extrinsic } from '@polkadot/types/interfaces';
|
||||
import { BlockNumber, EventRecord, Extrinsic } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { AddressMini, Call, Column, Expander, LinkExternal, Table } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import ExtrinsicDisplay from './Extrinsic';
|
||||
|
||||
interface Props {
|
||||
blockNumber?: BlockNumber;
|
||||
className?: string;
|
||||
events?: EventRecord[];
|
||||
label?: React.ReactNode;
|
||||
value?: Extrinsic[] | null;
|
||||
}
|
||||
|
||||
function getEra ({ era }: Extrinsic, blockNumber?: BlockNumber): [number, number] | null {
|
||||
if (blockNumber && era.isMortalEra) {
|
||||
const mortalEra = era.asMortalEra;
|
||||
|
||||
return [mortalEra.birth(blockNumber.toNumber()), mortalEra.death(blockNumber.toNumber())];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function Extrinsics ({ className, blockNumber, label, value }: Props): React.ReactElement<Props> {
|
||||
function Extrinsics ({ className, blockNumber, events, label, value }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Column className={className}>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{label || t('extrinsics')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('No pending extrinsics are in the queue')}>
|
||||
{value?.map((extrinsic, index): React.ReactNode => {
|
||||
const { meta, method, section } = registry.findMetaCall(extrinsic.callIndex);
|
||||
const era = getEra(extrinsic, blockNumber);
|
||||
|
||||
return (
|
||||
<tr key={`extrinsic:${index}`}>
|
||||
<td className='relative overflow'>
|
||||
{extrinsic.isSigned && (
|
||||
<div className='explorer--BlockByHash-header'>
|
||||
<div>
|
||||
<AddressMini value={extrinsic.signer} />
|
||||
</div>
|
||||
<div className='explorer--BlockByHash-nonce'>
|
||||
{t('index')} {formatNumber(extrinsic.nonce)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Expander
|
||||
summary={`${section}.${method} (#${formatNumber(index)})`}
|
||||
summaryMeta={meta}
|
||||
>
|
||||
<Call
|
||||
className='details'
|
||||
mortality={
|
||||
era
|
||||
? blockNumber
|
||||
? t('mortal, valid from #{{startAt}} to #{{endsAt}}', {
|
||||
replace: {
|
||||
endsAt: formatNumber(era[1]),
|
||||
startAt: formatNumber(era[0])
|
||||
}
|
||||
})
|
||||
: t('mortal')
|
||||
: t('immortal')
|
||||
}
|
||||
tip={extrinsic.tip?.toBn()}
|
||||
value={extrinsic}
|
||||
withHash
|
||||
/>
|
||||
</Expander>
|
||||
{
|
||||
extrinsic.isSigned
|
||||
? <LinkExternal data={extrinsic.hash.toHex()} type='extrinsic' />
|
||||
: null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</Column>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{label || t('extrinsics')}</h1></th>
|
||||
<th className='address'>{t('signer')}</th>
|
||||
<th className='start'>{t('events')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('No pending extrinsics are in the queue')}>
|
||||
{value?.map((extrinsic, index): React.ReactNode =>
|
||||
<ExtrinsicDisplay
|
||||
blockNumber={blockNumber}
|
||||
events={events}
|
||||
index={index}
|
||||
value={extrinsic}
|
||||
/>
|
||||
)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(Extrinsics)`
|
||||
.explorer--BlockByHash-header {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
}
|
||||
|
||||
.explorer--BlockByHash-nonce {
|
||||
font-size: 0.75rem;
|
||||
margin-left: 2.25rem;
|
||||
margin-top: -0.625rem;
|
||||
opacity: 0.45;
|
||||
text-align: left;
|
||||
}
|
||||
`);
|
||||
export default React.memo(Extrinsics);
|
||||
|
||||
@ -7,7 +7,7 @@ import { Codec, TypeDef } from '@polkadot/types/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Struct, Tuple, Raw, Vec, getTypeDef } from '@polkadot/types';
|
||||
import { Column, Expander, Table } from '@polkadot/react-components';
|
||||
import { Expander, Table } from '@polkadot/react-components';
|
||||
import Params from '@polkadot/react-params';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -103,29 +103,23 @@ function Logs (props: Props): React.ReactElement<Props> | null {
|
||||
const { value } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!value || !value.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('logs')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{value.map((log, index) => (
|
||||
<tr key={`log:${index}`}>
|
||||
<td className='overflow'>
|
||||
<Expander summary={log.type.toString()}>
|
||||
{formatItem(log)}
|
||||
</Expander>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</Column>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('logs')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{value?.map((log, index) => (
|
||||
<tr key={`log:${index}`}>
|
||||
<td className='overflow'>
|
||||
<Expander summary={log.type.toString()}>
|
||||
{formatItem(log)}
|
||||
</Expander>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,25 +2,21 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyedEvent } from './types';
|
||||
import { EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Event as EventDisplay, Expander } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
interface Props {
|
||||
event: KeyedEvent;
|
||||
withoutIndex?: boolean;
|
||||
className?: string;
|
||||
value: EventRecord;
|
||||
}
|
||||
|
||||
function Event ({ event: { record: { event, phase } }, withoutIndex }: Props): React.ReactElement<Props> {
|
||||
function Event ({ className, value: { event } }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<Expander
|
||||
summary={`${event.section}.${event.method} ${
|
||||
!withoutIndex && phase.isApplyExtrinsic
|
||||
? `(#${formatNumber(phase.asApplyExtrinsic)})`
|
||||
: ''
|
||||
}`}
|
||||
className={className}
|
||||
summary={`${event.section}.${event.method}`}
|
||||
summaryMeta={event.meta}
|
||||
>
|
||||
<EventDisplay
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyedEvent } from './types';
|
||||
import { EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
@ -12,13 +12,12 @@ import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
emptyLabel?: React.ReactNode;
|
||||
events: KeyedEvent[];
|
||||
events: EventRecord[];
|
||||
eventClassName?: string;
|
||||
label?: React.ReactNode;
|
||||
withoutIndex?: boolean;
|
||||
}
|
||||
|
||||
function Events ({ emptyLabel, eventClassName, events, label, withoutIndex }: Props): React.ReactElement<Props> {
|
||||
function Events ({ emptyLabel, eventClassName, events, label }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -28,17 +27,14 @@ function Events ({ emptyLabel, eventClassName, events, label, withoutIndex }: Pr
|
||||
</Table.Head>
|
||||
<Table.Body empty={emptyLabel || t('No events available')}>
|
||||
{events
|
||||
.filter(({ record: { event: { method, section } } }): boolean => !!method && !!section)
|
||||
.map((event: KeyedEvent): React.ReactNode => (
|
||||
.filter(({ event: { method, section } }): boolean => !!method && !!section)
|
||||
.map((event: EventRecord, index): React.ReactNode => (
|
||||
<tr
|
||||
className={eventClassName}
|
||||
key={event.key}
|
||||
key={`event:${index}`}
|
||||
>
|
||||
<td className='overflow'>
|
||||
<Event
|
||||
event={event}
|
||||
withoutIndex={withoutIndex}
|
||||
/>
|
||||
<Event value={event} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
||||
@ -28,7 +28,7 @@ function Main ({ events, headers }: Props): React.ReactElement<Props> {
|
||||
<BlockHeaders headers={headers} />
|
||||
</Column>
|
||||
<Column>
|
||||
<Events events={events} />
|
||||
<Events events={events.map(({ record }) => record)} />
|
||||
</Column>
|
||||
</Columar>
|
||||
</>
|
||||
|
||||
@ -28,15 +28,6 @@ class Column extends Collection<Props, State> {
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
// renderEmpty () {
|
||||
// const { emptyText } = this.props;
|
||||
// return (
|
||||
// <article className='container'>
|
||||
// {emptyText}
|
||||
// </article>
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
export default translate(
|
||||
@ -47,7 +38,15 @@ export default translate(
|
||||
max-width: 100%;
|
||||
flex: 1 1;
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
padding: 0 0.5rem;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
|
||||
@ -242,6 +242,10 @@ const Memo = React.memo(styled(Table)`
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.top {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user