import * as React from 'react'; import { Types, SortedCollection } from '@dotstats/common'; import { AllChains, Chains, Chain, Ago, OfflineIndicator } from './components'; import { Connection } from './Connection'; import { PersistentObject, PersistentSet } from './persist'; import { State, Node, ChainData } from './state'; import { getHashData } from './utils'; import stable from 'stable'; import './App.css'; export default class App extends React.Component<{}, State> { public state: State; private readonly settings: PersistentObject; private readonly pins: PersistentSet; private readonly connection: Promise; constructor(props: {}) { super(props); this.settings = new PersistentObject( 'settings', { validator: true, location: true, implementation: true, networkId: false, peers: true, txs: true, cpu: true, mem: true, upload: false, download: false, blocknumber: true, blockhash: true, blocktime: true, finalized: false, finalizedhash: false, blockpropagation: true, blocklasttime: false, networkstate: false, }, (settings) => this.setState({ settings }) ); this.pins = new PersistentSet('pinned_names', (pins) => { const { nodes } = this.state; nodes.mutEachAndSort((node) => node.setPinned(pins.has(node.name))); this.setState({ nodes, pins }); }); const { tab = '' } = getHashData(); this.state = { status: 'offline', best: 0 as Types.BlockNumber, finalized: 0 as Types.BlockNumber, consensusInfo: new Array() as Types.ConsensusInfo, displayConsensusLoadingScreen: true, authorities: new Array() as Types.Authorities, authoritySetId: null, sendFinality: false, blockTimestamp: 0 as Types.Timestamp, blockAverage: null, timeDiff: 0 as Types.Milliseconds, subscribed: null, chains: new Map(), nodes: new SortedCollection(Node.compare), settings: this.settings.raw(), pins: this.pins.get(), tab, }; this.connection = Connection.create(this.pins, (changes) => { if (changes) { this.setState(changes); } return this.state; }); } public render() { const { timeDiff, subscribed, status, tab } = this.state; const chains = this.chains(); Ago.timeDiff = timeDiff; if (chains.length === 0) { return (
Waiting for telemetry…
); } const overlay = tab === 'all-chains' ? : null; return (
{overlay}
); } public componentWillMount() { window.addEventListener('keydown', this.onKeyPress); window.addEventListener('hashchange', this.onHashChange); } public componentWillUnmount() { window.removeEventListener('keydown', this.onKeyPress); window.removeEventListener('hashchange', this.onHashChange); } private onKeyPress = (event: KeyboardEvent) => { if (event.keyCode !== 9) { // TAB KEY return; } event.preventDefault(); const { subscribed } = this.state; const chains = Array.from(this.state.chains.keys()); let index = 0; if (subscribed) { index = (chains.indexOf(subscribed) + 1) % chains.length; // Do nothing if it's the same chain if (chains[index] === subscribed) { return; } } this.connection.then((connection) => { connection.subscribe(chains[index]); }) } private onHashChange = (event: Event) => { const { tab = '' } = getHashData(); this.setState({ tab }); } private chains(): ChainData[] { return stable .inplace( Array.from(this.state.chains.entries()), (a, b) => { if (a[0] === 'Alexander') { return -1; } if (b[0] === 'Alexander') { return 1; } return b[1] - a[1]; } ) .map(([label, nodeCount]) => ({ label, nodeCount })); } }