// Source code for the Substrate Telemetry Server. // Copyright (C) 2021 Parity Technologies (UK) Ltd. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . import * as React from 'react'; import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision, } from '../../utils'; import { Ago, Icon, PolkadotIcon } from '../'; import { Node } from '../../state'; import nodeIcon from '../../icons/server.svg'; import nodeValidatorIcon from '../../icons/shield.svg'; import nodeTypeIcon from '../../icons/terminal.svg'; import nodeLocationIcon from '../../icons/location.svg'; import blockIcon from '../../icons/package.svg'; import blockHashIcon from '../../icons/file-binary.svg'; import blockTimeIcon from '../../icons/history.svg'; import propagationTimeIcon from '../../icons/dashboard.svg'; import lastTimeIcon from '../../icons/watch.svg'; import './Location.css'; export namespace Location { export type Quarter = 0 | 1 | 2 | 3; export interface Props { node: Node; position: Position; focused: boolean; } export interface Position { left: number; top: number; quarter: Quarter; } export interface State { hover: boolean; } } export class Location extends React.Component { public readonly state = { hover: false }; public render() { const { node, position, focused } = this.props; const { left, top, quarter } = position; const { height, propagationTime, city } = node; if (!city) { return null; } let className = `Location Location-quarter${quarter}`; if (focused) { if (propagationTime != null) { className += ' Location-synced'; } else if (height % 2 === 1) { className += ' Location-odd'; } } else { className += ' Location-dimmed'; } return (
{this.state.hover ? this.renderDetails() : null}
); } private renderDetails() { const { name, implementation, version, validator, height, hash, blockTime, blockTimestamp, propagationTime, city, } = this.props.node; let validatorRow =
; if (validator) { validatorRow = ( {trimHash(validator, 30)} ); } return ( {validatorRow}
{name}
{implementation} v{version}
{city}
#{formatNumber(height)}
{trimHash(hash, 20)}
{secondsWithPrecision(blockTime / 1000)} {propagationTime == null ? '∞' : milliOrSecond(propagationTime)}
); } private onMouseOver = () => { this.setState({ hover: true }); }; private onMouseOut = () => { this.setState({ hover: false }); }; }