diff --git a/packages/app-explorer/src/BlockHeaders.tsx b/packages/app-explorer/src/BlockHeaders.tsx index 90021cfa29..e140d16002 100644 --- a/packages/app-explorer/src/BlockHeaders.tsx +++ b/packages/app-explorer/src/BlockHeaders.tsx @@ -2,61 +2,26 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { CallProps } from '@polkadot/react-api/types'; - import React from 'react'; import { HeaderExtended } from '@polkadot/api-derive'; -import { withCalls } from '@polkadot/react-api'; import BlockHeader from './BlockHeader'; -export const MAX_ITEMS = 15; - -let blockHeaders: HeaderExtended[] = []; - -const transform = (header: HeaderExtended): HeaderExtended[] => { - if (!header) { - return blockHeaders; - } - - blockHeaders = blockHeaders - .filter((old, index): boolean => - index < MAX_ITEMS && old.number.unwrap().lt(header.number.unwrap()) - ) - .reduce((next, header): HeaderExtended[] => { - next.push(header); - - return next; - }, [header]) - .sort((a, b): number => - b.number.unwrap().cmp(a.number.unwrap()) - ); - - return blockHeaders; -}; - -interface Props extends CallProps { - headers?: HeaderExtended[]; +interface Props { + headers: HeaderExtended[]; } -function BlockHeaders ({ headers = [] }: Props): React.ReactElement { +export default function BlockHeaders ({ headers }: Props): React.ReactElement { return ( <> - { - headers.map((header, index): React.ReactNode => ( - - )) - } + {headers.map((header, index): React.ReactNode => ( + + ))} ); } - -export default withCalls(['derive.chain.subscribeNewHeads', { - propName: 'headers', - transform -}])(BlockHeaders); diff --git a/packages/app-explorer/src/Events.tsx b/packages/app-explorer/src/Events.tsx index 5b8e9fca57..c5bc74496d 100644 --- a/packages/app-explorer/src/Events.tsx +++ b/packages/app-explorer/src/Events.tsx @@ -29,49 +29,47 @@ function Events ({ emptyLabel, eventClassName, events, withoutIndex, t }: Props) return ( <> - { - events.map( - ({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => { - const extIndex = !withoutIndex && phase.isApplyExtrinsic - ? phase.asApplyExtrinsic - : -1; + {events.map( + ({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => { + const extIndex = !withoutIndex && phase.isApplyExtrinsic + ? phase.asApplyExtrinsic + : -1; - if (!event.method || !event.section) { - return null; - } - - return ( -
-
-

- {event.section}.{event.method} { - extIndex !== -1 - ? `(#${formatNumber(extIndex)})` - : '' - } -

-
-
- - { - event.meta && event.meta.documentation - ? event.meta.documentation.join(' ') - : 'Details' - } - - -
-
- ); + if (!event.method || !event.section) { + return null; } - ) - } + + return ( +
+
+

+ {event.section}.{event.method} { + extIndex !== -1 + ? `(#${formatNumber(extIndex)})` + : '' + } +

+
+
+ + { + event.meta && event.meta.documentation + ? event.meta.documentation.join(' ') + : 'Details' + } + + +
+
+ ); + } + )} ); } diff --git a/packages/app-explorer/src/Main.tsx b/packages/app-explorer/src/Main.tsx index ed1379f830..ddab0cc239 100644 --- a/packages/app-explorer/src/Main.tsx +++ b/packages/app-explorer/src/Main.tsx @@ -6,6 +6,7 @@ import { I18nProps } from '@polkadot/react-components/types'; import { KeyedEvent } from './types'; import React from 'react'; +import { HeaderExtended } from '@polkadot/api-derive'; import { Columar, Column } from '@polkadot/react-components'; import BlockHeaders from './BlockHeaders'; @@ -16,16 +17,17 @@ import translate from './translate'; interface Props extends I18nProps { events: KeyedEvent[]; + headers: HeaderExtended[]; } -function Main ({ events, t }: Props): React.ReactElement { +function Main ({ events, headers, t }: Props): React.ReactElement { return ( <> - + diff --git a/packages/app-explorer/src/index.tsx b/packages/app-explorer/src/index.tsx index 0fcb511f97..ad99205f5a 100644 --- a/packages/app-explorer/src/index.tsx +++ b/packages/app-explorer/src/index.tsx @@ -1,132 +1,137 @@ -/* eslint-disable @typescript-eslint/camelcase */ // Copyright 2017-2019 @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 { ApiProps } from '@polkadot/react-api/types'; import { AppProps, BareProps, I18nProps } from '@polkadot/react-components/types'; import { EventRecord } from '@polkadot/types/interfaces'; import { KeyedEvent } from './types'; import './index.css'; -import React from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Route, Switch } from 'react-router'; import styled from 'styled-components'; -import { withCalls, withMulti } from '@polkadot/react-api'; -import Tabs, { TabItem } from '@polkadot/react-components/Tabs'; +import { HeaderExtended } from '@polkadot/api-derive'; +import { ApiContext, withCalls, withMulti } from '@polkadot/react-api'; +import Tabs from '@polkadot/react-components/Tabs'; import uiSettings from '@polkadot/ui-settings'; import { stringToU8a } from '@polkadot/util'; import { xxhashAsHex } from '@polkadot/util-crypto'; -import { MAX_ITEMS } from './BlockHeaders'; import BlockInfo from './BlockInfo'; import Forks from './Forks'; import Main from './Main'; import NodeInfo from './NodeInfo'; import translate from './translate'; -interface Props extends AppProps, BareProps, I18nProps { - system_events?: EventRecord[]; +interface Props extends ApiProps, AppProps, BareProps, I18nProps { + newHeader?: HeaderExtended; + newEvents?: KeyedEvent[]; } -interface State { - items: TabItem[]; - prevEventHash: string; - recentEvents: KeyedEvent[]; -} +const MAX_ITEMS = 15; -class ExplorerApp extends React.Component { - public constructor (props: Props) { - super(props); +function ExplorerApp ({ basePath, className, newEvents, newHeader, t }: Props): React.ReactElement { + const [headers, setHeaders] = useState([]); + const [{ prevEventHash, events }, setEvents] = useState<{ prevEventHash: string; events: KeyedEvent[] }>({ prevEventHash: '', events: [] }); + const { api } = useContext(ApiContext); - const { t } = this.props; + useEffect((): void => { + const newEventHash = xxhashAsHex(stringToU8a(JSON.stringify(newEvents))); - this.state = { - items: [ - { - isRoot: true, - name: 'chain', - text: t('Chain info') - }, - { - hasParams: true, - name: 'query', - text: t('Block details') - }, - { - name: 'node', - text: t('Node info') - } - ], - prevEventHash: '', - recentEvents: [] - }; - } - - // assign the events on the index - this way we keep a record of these, even when swapping - // tabs withing this app, all the events as received will be shown - public static getDerivedStateFromProps ({ system_events = [] }: Props, prevState: State): State | null { - const prevEventHash = xxhashAsHex(stringToU8a(JSON.stringify(system_events))); - - if (prevEventHash === prevState.prevEventHash) { - return null; + if (newEventHash === prevEventHash || !newEvents) { + return; } - const recentEvents = system_events - .filter(({ event }): boolean => event.section !== 'system') - .map((record, index): { key: string; record: EventRecord } => ({ key: `${Date.now()}-${index}`, record })) - .concat(prevState.recentEvents) - .filter((_, index): boolean => index < MAX_ITEMS); + setEvents({ + prevEventHash: newEventHash, + events: newEvents.concat(events).filter((_, index): boolean => index < MAX_ITEMS) + }); + }, [newEvents]); - return { - items: prevState.items, - prevEventHash, - recentEvents - }; - } + useEffect((): void => { + if (!newHeader) { + return; + } - public render (): React.ReactNode { - const { basePath, className } = this.props; - const { items } = this.state; - const hidden = uiSettings.uiMode === 'full' - ? [] - : ['node']; + setHeaders( + headers + .filter((old, index): boolean => index < MAX_ITEMS && old.number.unwrap().lt(newHeader.number.unwrap())) + .reduce((next, header): HeaderExtended[] => { + next.push(header); - return ( -
-
-
- - - - - - - -
+ return next; + }, [newHeader]) + .sort((a, b): number => b.number.unwrap().cmp(a.number.unwrap())) ); - } + }, [newHeader]); - private renderMain = (): JSX.Element => { - const { recentEvents } = this.state; - - return ( -
- ); - } + return ( +
+
+
+ + + + + + => +
+ } + /> + +
+ ); } export default withMulti( - styled(ExplorerApp as React.ComponentClass)` + styled(ExplorerApp)` .rx--updated { background: transparent !important; } `, translate, - withCalls('query.system.events') + withCalls( + ['query.system.events', { + propName: 'newEvents', + transform: (records: EventRecord[]): KeyedEvent[] => + records + .filter(({ event }): boolean => event.section !== 'system') + .map((record, index): KeyedEvent => ({ key: `${Date.now()}-${index}`, record })) + }], + ['derive.chain.subscribeNewHeads', { propName: 'newHeader' }] + ) ); diff --git a/packages/react-query/src/TimePeriod.tsx b/packages/react-query/src/TimePeriod.tsx index 218a36af9a..e4fae5c064 100644 --- a/packages/react-query/src/TimePeriod.tsx +++ b/packages/react-query/src/TimePeriod.tsx @@ -37,7 +37,7 @@ export function TimePeriod ({ babe_expectedBlockTime, children, className, label // NOTE we are not combining this into one with a fallback, it becomes slightly tricky. // There must be a cleaner way with this logic, but as of now it works on Alex & Kusama // - babe bas the exact value in miliseconds -// - non-babe on 2.x has the value in miliseconds (not catered for here) +// - non-babe on 2.x has the value in miliseconds (not catered for here) // - anything non-babe, i.e. Aura, needs to be multiplied by 2 export default withCalls( // substrate 2.x: consts.babe.expectedBlockTime