From ba545799fec3294fa9013f524a8bcc9e55ba8be7 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Fri, 10 Aug 2018 14:36:05 +0200 Subject: [PATCH] Reorganize Node components and state --- package-lock.json | 4 + packages/backend/src/Node.ts | 4 + packages/backend/src/message.ts | 1 + packages/common/src/types.ts | 1 + packages/frontend/src/Connection.ts | 8 +- packages/frontend/src/components/Chain.tsx | 6 +- packages/frontend/src/components/Node.tsx | 156 ------------------ .../{Node.css => Node/Location.css} | 15 +- .../frontend/src/components/Node/Location.tsx | 102 ++++++++++++ packages/frontend/src/components/Node/Row.css | 7 + packages/frontend/src/components/Node/Row.tsx | 66 ++++++++ .../frontend/src/components/Node/index.ts | 7 + packages/frontend/src/components/index.ts | 5 +- packages/frontend/src/state.ts | 13 +- 14 files changed, 218 insertions(+), 177 deletions(-) create mode 100644 package-lock.json delete mode 100644 packages/frontend/src/components/Node.tsx rename packages/frontend/src/components/{Node.css => Node/Location.css} (82%) create mode 100644 packages/frontend/src/components/Node/Location.tsx create mode 100644 packages/frontend/src/components/Node/Row.css create mode 100644 packages/frontend/src/components/Node/Row.tsx create mode 100644 packages/frontend/src/components/Node/index.ts diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..62ca6a7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4 @@ +{ + "version": "0.1.0", + "lockfileVersion": 1 +} diff --git a/packages/backend/src/Node.ts b/packages/backend/src/Node.ts index fd5b10e..f095d9c 100644 --- a/packages/backend/src/Node.ts +++ b/packages/backend/src/Node.ts @@ -119,6 +119,8 @@ export default class Node { function handler(data: WebSocket.Data) { const message = parseMessage(data); + console.log(message); + if (!message || !message.msg) { return; } @@ -198,6 +200,8 @@ export default class Node { } private onMessage(message: Message) { + console.log(message); + this.lastMessage = timestamp(); const update = getBestBlock(message); diff --git a/packages/backend/src/message.ts b/packages/backend/src/message.ts index d15d84e..c41708d 100644 --- a/packages/backend/src/message.ts +++ b/packages/backend/src/message.ts @@ -46,6 +46,7 @@ interface SystemConnected { config: string, implementation: Types.NodeImplementation, version: Types.NodeVersion, + pubkey: Maybe, } export interface SystemInterval extends BestBlock { diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index cf9dc1b..692ee46 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -8,6 +8,7 @@ export type NodeId = Id<'Node'>; export type NodeName = Opaque; export type NodeImplementation = Opaque; export type NodeVersion = Opaque; +export type NodePubKey = Opaque; export type BlockNumber = Opaque; export type BlockHash = Opaque; export type Milliseconds = Opaque; diff --git a/packages/frontend/src/Connection.ts b/packages/frontend/src/Connection.ts index 6e47a3a..c47baeb 100644 --- a/packages/frontend/src/Connection.ts +++ b/packages/frontend/src/Connection.ts @@ -11,11 +11,11 @@ export class Connection { return new Connection(await Connection.socket(), update); } - private static readonly address = window.location.protocol === 'https:' - ? `wss://${window.location.hostname}/feed/` - : `ws://${window.location.hostname}:8080`; + // private static readonly address = window.location.protocol === 'https:' + // ? `wss://${window.location.hostname}/feed/` + // : `ws://${window.location.hostname}:8080`; - // private static readonly address = 'wss://telemetry.polkadot.io/feed/'; + private static readonly address = 'wss://telemetry.polkadot.io/feed/'; private static async socket(): Promise { let socket = await Connection.trySocket(); diff --git a/packages/frontend/src/components/Chain.tsx b/packages/frontend/src/components/Chain.tsx index be6a7f9..ed6bc2c 100644 --- a/packages/frontend/src/components/Chain.tsx +++ b/packages/frontend/src/components/Chain.tsx @@ -31,7 +31,7 @@ export namespace Chain { } } -function sortNodes(a: Node.Props, b: Node.Props): number { +function sortNodes(a: AppState.Node, b: AppState.Node): number { if (a.blockDetails[0] === b.blockDetails[0]) { const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number; const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number; @@ -131,7 +131,7 @@ export class Chain extends React.Component { private renderTable() { return ( - + { this.nodes().sort(sortNodes).map((node) => ) @@ -145,7 +145,7 @@ export class Chain extends React.Component { return Array.from(this.props.appState.nodes.values()); } - private pixelPosition(lat: Types.Latitude, lon: Types.Longitude): Node.PixelPosition { + private pixelPosition(lat: Types.Latitude, lon: Types.Longitude): Node.Location.Position { const { map } = this.state; // Longitude ranges -180 (west) to +180 (east) diff --git a/packages/frontend/src/components/Node.tsx b/packages/frontend/src/components/Node.tsx deleted file mode 100644 index f578a5d..0000000 --- a/packages/frontend/src/components/Node.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import * as React from 'react'; -import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision } from '../utils'; -import { Ago, Icon } from './'; -import { Types, Maybe } from '@dotstats/common'; - -import nodeIcon from '../icons/server.svg'; -import nodeTypeIcon from '../icons/terminal.svg'; -import nodeLocationIcon from '../icons/location.svg'; -import peersIcon from '../icons/broadcast.svg'; -import transactionsIcon from '../icons/inbox.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 './Node.css'; - -const SEMVER_PATTERN = /^\d+\.\d+\.\d+/; - -export namespace Node { - export interface Props { - id: Types.NodeId; - nodeDetails: Types.NodeDetails; - nodeStats: Types.NodeStats; - blockDetails: Types.BlockDetails; - location: Maybe; - } - - export interface PixelPosition { - left: number; - top: number; - } - - export interface LocationState { - hover: boolean; - } - - export function Header() { - return ( - - - - - - - - - - - - - - ) - } - - export function Row(props: Props) { - const [name, implementation, version] = props.nodeDetails; - const [height, hash, blockTime, blockTimestamp, propagationTime] = props.blockDetails; - const [peers, txcount] = props.nodeStats; - const [semver] = version.match(SEMVER_PATTERN) || [version]; - - let className = 'Node-Row'; - - if (propagationTime != null) { - className += ' Node-Row-synced'; - } - - return ( - - - - - - - - - - - - ); - } - - export class Location extends React.Component { - public readonly state = { hover: false }; - - public render() { - const { left, top, location } = this.props; - const height = this.props.blockDetails[0]; - const propagationTime = this.props.blockDetails[4]; - - if (!location) { - return null; - } - - let className = 'Node-Location'; - - if (propagationTime != null) { - className += ' Node-Location-synced'; - } else if (height % 2 === 1) { - className += ' Node-Location-odd'; - } - - return ( -
- { - this.state.hover ? this.renderDetails(location) : null - } -
- ); - } - - private renderDetails(location: Types.NodeLocation) { - const [name, implementation, version] = this.props.nodeDetails; - const [height, hash, blockTime, blockTimestamp, propagationTime] = this.props.blockDetails; - - return ( -
{name}{implementation} v{semver}{peers}{txcount}#{formatNumber(height)}{trimHash(hash, 16)}{secondsWithPrecision(blockTime/1000)}{propagationTime === null ? '∞' : milliOrSecond(propagationTime as number)}
- - - - - - - - - - - - - - - - - - - - - - - - - -
{name}
{implementation} v{version}
{location[2]}
#{formatNumber(height)}
{trimHash(hash, 20)}
{secondsWithPrecision(blockTime/1000)}{propagationTime === null ? '∞' : milliOrSecond(propagationTime as number)}
- ); - } - - private onMouseOver = () => { - this.setState({ hover: true }); - } - - private onMouseOut = () => { - this.setState({ hover: false }); - } - } -} diff --git a/packages/frontend/src/components/Node.css b/packages/frontend/src/components/Node/Location.css similarity index 82% rename from packages/frontend/src/components/Node.css rename to packages/frontend/src/components/Node/Location.css index 7e11b43..8eff339 100644 --- a/packages/frontend/src/components/Node.css +++ b/packages/frontend/src/components/Node/Location.css @@ -1,10 +1,3 @@ -.Node-Row { - color: #999; -} - -.Node-Row-synced { - color: #fff; -} .Node-Location { width: 6px; @@ -36,7 +29,7 @@ border-color: #fff; } -.Node-details { +.Node-Location-details { min-width: 335px; position: absolute; left: 16px; @@ -48,17 +41,17 @@ border-collapse: collapse; } -.Node-details td { +.Node-Location-details td { text-align: left; padding: 0.5em 1em; } -.Node-details td:nth-child(odd) { +.Node-Location-details td:nth-child(odd) { width: 16px; text-align: center; padding-right: 0.2em; } -.Node-details td:nth-child(even) { +.Node-Location-details td:nth-child(even) { padding-left: 0.2em; } diff --git a/packages/frontend/src/components/Node/Location.tsx b/packages/frontend/src/components/Node/Location.tsx new file mode 100644 index 0000000..a3059b5 --- /dev/null +++ b/packages/frontend/src/components/Node/Location.tsx @@ -0,0 +1,102 @@ +import * as React from 'react'; +import { Types } from '@dotstats/common'; +import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision } from '../../utils'; +import { Ago, Icon } from '../'; +import { State as AppState } from '../../state'; + +import nodeIcon from '../../icons/server.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'; + +namespace Location { + export interface Position { + left: number; + top: number; + } + + export interface State { + hover: boolean; + } +} + +class Location extends React.Component { + public readonly state = { hover: false }; + + public render() { + const { left, top, location } = this.props; + const height = this.props.blockDetails[0]; + const propagationTime = this.props.blockDetails[4]; + + if (!location) { + return null; + } + + let className = 'Node-Location'; + + if (propagationTime != null) { + className += ' Node-Location-synced'; + } else if (height % 2 === 1) { + className += ' Node-Location-odd'; + } + + return ( +
+ { + this.state.hover ? this.renderDetails(location) : null + } +
+ ); + } + + private renderDetails(location: Types.NodeLocation) { + const [name, implementation, version] = this.props.nodeDetails; + const [height, hash, blockTime, blockTimestamp, propagationTime] = this.props.blockDetails; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + +
{name}
{implementation} v{version}
{location[2]}
#{formatNumber(height)}
{trimHash(hash, 20)}
{secondsWithPrecision(blockTime/1000)}{propagationTime === null ? '∞' : milliOrSecond(propagationTime as number)}
+ ); + } + + private onMouseOver = () => { + this.setState({ hover: true }); + } + + private onMouseOut = () => { + this.setState({ hover: false }); + } +} + +export default Location; diff --git a/packages/frontend/src/components/Node/Row.css b/packages/frontend/src/components/Node/Row.css new file mode 100644 index 0000000..b483779 --- /dev/null +++ b/packages/frontend/src/components/Node/Row.css @@ -0,0 +1,7 @@ +.Node-Row { + color: #999; +} + +.Node-Row-synced { + color: #fff; +} diff --git a/packages/frontend/src/components/Node/Row.tsx b/packages/frontend/src/components/Node/Row.tsx new file mode 100644 index 0000000..73fe9c8 --- /dev/null +++ b/packages/frontend/src/components/Node/Row.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision } from '../../utils'; +import { State as AppState } from '../../state'; +import { SEMVER_PATTERN } from './'; +import { Ago, Icon } from '../'; + +import nodeIcon from '../../icons/server.svg'; +import nodeTypeIcon from '../../icons/terminal.svg'; +import peersIcon from '../../icons/broadcast.svg'; +import transactionsIcon from '../../icons/inbox.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 './Row.css'; + +export default class Row extends React.Component { + public static Header = () => { + return ( + + + + + + + + + + + + + + ) + } + + public render() { + const { nodeDetails, blockDetails, nodeStats } = this.props; + + const [name, implementation, version] = nodeDetails; + const [height, hash, blockTime, blockTimestamp, propagationTime] = blockDetails; + const [peers, txcount] = nodeStats; + const [semver] = version.match(SEMVER_PATTERN) || [version]; + + let className = 'Node-Row'; + + if (propagationTime != null) { + className += ' Node-Row-synced'; + } + + return ( + + {name} + {implementation} v{semver} + {peers} + {txcount} + #{formatNumber(height)} + {trimHash(hash, 16)} + {secondsWithPrecision(blockTime/1000)} + {propagationTime === null ? '∞' : milliOrSecond(propagationTime as number)} + + + ); + } +} diff --git a/packages/frontend/src/components/Node/index.ts b/packages/frontend/src/components/Node/index.ts new file mode 100644 index 0000000..d08625a --- /dev/null +++ b/packages/frontend/src/components/Node/index.ts @@ -0,0 +1,7 @@ +import { Types, Maybe } from '@dotstats/common'; +import Row from './Row'; +import Location from './Location'; + +export { Row, Location }; + +export const SEMVER_PATTERN = /^\d+\.\d+\.\d+/; diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 47c98ad..48199a8 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -1,7 +1,10 @@ export * from './Chains'; export * from './Chain'; export * from './Icon'; -export * from './Node'; export * from './Tile'; export * from './Ago'; export * from './OfflineIndicator'; + +import * as Node from './Node'; + +export { Node }; diff --git a/packages/frontend/src/state.ts b/packages/frontend/src/state.ts index 9996435..34d4994 100644 --- a/packages/frontend/src/state.ts +++ b/packages/frontend/src/state.ts @@ -1,6 +1,15 @@ -import { Node } from './components/Node'; import { Types, Maybe } from '@dotstats/common'; +export namespace State { + export interface Node { + id: Types.NodeId; + nodeDetails: Types.NodeDetails; + nodeStats: Types.NodeStats; + blockDetails: Types.BlockDetails; + location: Maybe; + } +} + export interface State { status: 'online' | 'offline' | 'upgrade-requested'; best: Types.BlockNumber; @@ -9,7 +18,7 @@ export interface State { timeDiff: Types.Milliseconds; subscribed: Maybe; chains: Map; - nodes: Map; + nodes: Map; } export type Update = (changes: Pick | null) => Readonly;