mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-11-01 15:11:08 +00:00
Working on map view
This commit is contained in:
1 parent
bcc7145477
commit
2c073eac19
16 files changed
+308
-64
No files matched your search
@@ -44,6 +44,7 @@ export default class Chain {
|
||||
|
||||
node.events.on('block', () => this.updateBlock(node));
|
||||
node.events.on('stats', () => this.feeds.broadcast(Feed.stats(node)));
|
||||
node.events.on('location', (location) => this.feeds.broadcast(Feed.locatedNode(node, location)));
|
||||
}
|
||||
|
||||
public addFeed(feed: Feed) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as EventEmitter from 'events';
|
||||
import Node from './Node';
|
||||
import Chain from './Chain';
|
||||
import { VERSION, timestamp, Maybe, FeedMessage, Types, idGenerator } from '@dotstats/common';
|
||||
import { Location } from './location';
|
||||
|
||||
const nextId = idGenerator<Types.FeedId>();
|
||||
const { Actions } = FeedMessage;
|
||||
@@ -42,7 +43,7 @@ export default class Feed {
|
||||
public static addedNode(node: Node): FeedMessage.Message {
|
||||
return {
|
||||
action: Actions.AddedNode,
|
||||
payload: [node.id, node.nodeDetails(), node.nodeStats(), node.blockDetails()]
|
||||
payload: [node.id, node.nodeDetails(), node.nodeStats(), node.blockDetails(), node.nodeLocation()]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,6 +54,13 @@ export default class Feed {
|
||||
};
|
||||
}
|
||||
|
||||
public static locatedNode(node: Node, location: Location): FeedMessage.Message {
|
||||
return {
|
||||
action: Actions.LocatedNode,
|
||||
payload: [node.id, location.lat, location.lon]
|
||||
};
|
||||
}
|
||||
|
||||
public static imported(node: Node): FeedMessage.Message {
|
||||
return {
|
||||
action: Actions.ImportedBlock,
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import * as EventEmitter from 'events';
|
||||
import * as iplocation from 'iplocation';
|
||||
import { timestamp, Maybe, Types, idGenerator } from '@dotstats/common';
|
||||
import { parseMessage, getBestBlock, Message, BestBlock, SystemInterval } from './message';
|
||||
import { locate, Location } from './location';
|
||||
|
||||
const BLOCK_TIME_HISTORY = 10;
|
||||
const TIMEOUT = (1000 * 60 * 1) as Types.Milliseconds; // 1 minute
|
||||
|
||||
const nextId = idGenerator<Types.NodeId>();
|
||||
|
||||
export interface NodeEvents {
|
||||
on(event: 'location', fn: (location: Location) => void): void;
|
||||
emit(event: 'location', location: Location): void;
|
||||
}
|
||||
|
||||
export default class Node {
|
||||
public readonly id: Types.NodeId;
|
||||
public readonly name: Types.NodeName;
|
||||
@@ -16,8 +21,9 @@ export default class Node {
|
||||
public readonly implementation: Types.NodeImplementation;
|
||||
public readonly version: Types.NodeVersion;
|
||||
|
||||
public readonly events = new EventEmitter();
|
||||
public readonly events = new EventEmitter() as EventEmitter & NodeEvents;
|
||||
|
||||
public location: Maybe<Location> = null;
|
||||
public lastMessage: Types.Timestamp;
|
||||
public config: string;
|
||||
public best = '' as Types.BlockHash;
|
||||
@@ -30,11 +36,13 @@ export default class Node {
|
||||
private peers = 0 as Types.PeerCount;
|
||||
private txcount = 0 as Types.TransactionCount;
|
||||
|
||||
private readonly ip: string;
|
||||
private readonly socket: WebSocket;
|
||||
private blockTimes: Array<number> = new Array(BLOCK_TIME_HISTORY);
|
||||
private lastBlockAt: Maybe<Date> = null;
|
||||
|
||||
constructor(
|
||||
ip: string,
|
||||
socket: WebSocket,
|
||||
name: Types.NodeName,
|
||||
chain: Types.ChainLabel,
|
||||
@@ -42,6 +50,7 @@ export default class Node {
|
||||
implentation: Types.NodeImplementation,
|
||||
version: Types.NodeVersion,
|
||||
) {
|
||||
this.ip = ip;
|
||||
this.id = nextId();
|
||||
this.name = name;
|
||||
this.chain = chain;
|
||||
@@ -83,6 +92,18 @@ export default class Node {
|
||||
|
||||
this.disconnect();
|
||||
});
|
||||
|
||||
locate(ip).then((location) => {
|
||||
if (!location) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('node', ip, 'located at', location);
|
||||
|
||||
this.location = location;
|
||||
|
||||
this.events.emit('location', location);
|
||||
});
|
||||
}
|
||||
|
||||
public static fromSocket(socket: WebSocket, ip: string): Promise<Node> {
|
||||
@@ -102,7 +123,7 @@ export default class Node {
|
||||
|
||||
const { name, chain, config, implementation, version } = message;
|
||||
|
||||
resolve(new Node(socket, name, chain, config, implementation, version));
|
||||
resolve(new Node(ip, socket, name, chain, config, implementation, version));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +157,12 @@ export default class Node {
|
||||
return [this.height, this.best, this.blockTime, this.blockTimestamp, this.propagationTime];
|
||||
}
|
||||
|
||||
public nodeLocation(): Maybe<Types.NodeLocation> {
|
||||
const { location } = this;
|
||||
|
||||
return location ? [location.lat, location.lon] : null;
|
||||
}
|
||||
|
||||
public get average(): number {
|
||||
let accounted = 0;
|
||||
let sum = 0;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as iplocation from 'iplocation';
|
||||
import { Maybe, Types } from '@dotstats/common';
|
||||
|
||||
export interface Location {
|
||||
lat: Types.Latitude;
|
||||
lon: Types.Longitude;
|
||||
}
|
||||
|
||||
const cache = new Map<string, Location>();
|
||||
|
||||
export async function locate(ip: string): Promise<Maybe<Location>> {
|
||||
if (ip === '127.0.0.1') {
|
||||
return Promise.resolve({
|
||||
lat: 52.5166667 as Types.Latitude,
|
||||
lon: 13.4 as Types.Longitude
|
||||
});
|
||||
}
|
||||
|
||||
const cached = cache.get(ip);
|
||||
|
||||
if (cached) {
|
||||
return Promise.resolve(cached);
|
||||
}
|
||||
|
||||
return new Promise<Maybe<Location>>((resolve, _) => {
|
||||
iplocation(ip, (err, result) => {
|
||||
if (err) {
|
||||
console.error(`Couldn't locate ${ip}`);
|
||||
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
const { lat, lon } = result;
|
||||
const location = { lat, lon } as Location;
|
||||
|
||||
cache.set(ip, location);
|
||||
|
||||
resolve(location);
|
||||
});
|
||||
})
|
||||
}
|
||||
+22
-12
@@ -1,10 +1,13 @@
|
||||
import { Opaque, Maybe } from './helpers';
|
||||
import {
|
||||
FeedVersion,
|
||||
Latitude,
|
||||
Longitude,
|
||||
NodeId,
|
||||
NodeCount,
|
||||
NodeDetails,
|
||||
NodeStats,
|
||||
NodeLocation,
|
||||
BlockNumber,
|
||||
BlockDetails,
|
||||
Timestamp,
|
||||
@@ -13,17 +16,18 @@ import {
|
||||
} from './types';
|
||||
|
||||
export const Actions = {
|
||||
FeedVersion: 255 as 255,
|
||||
BestBlock: 0 as 0,
|
||||
AddedNode: 1 as 1,
|
||||
RemovedNode: 2 as 2,
|
||||
ImportedBlock: 3 as 3,
|
||||
NodeStats: 4 as 4,
|
||||
TimeSync: 5 as 5,
|
||||
AddedChain: 6 as 6,
|
||||
RemovedChain: 7 as 7,
|
||||
SubscribedTo: 8 as 8,
|
||||
UnsubscribedFrom: 9 as 9
|
||||
FeedVersion : 0xff as 0xff,
|
||||
BestBlock : 0x00 as 0x00,
|
||||
AddedNode : 0x01 as 0x01,
|
||||
RemovedNode : 0x02 as 0x02,
|
||||
LocatedNode : 0x03 as 0x03,
|
||||
ImportedBlock : 0x04 as 0x04,
|
||||
NodeStats : 0x05 as 0x05,
|
||||
TimeSync : 0x06 as 0x06,
|
||||
AddedChain : 0x07 as 0x07,
|
||||
RemovedChain : 0x08 as 0x08,
|
||||
SubscribedTo : 0x09 as 0x09,
|
||||
UnsubscribedFrom : 0x0A as 0x0A,
|
||||
};
|
||||
|
||||
export type Action = typeof Actions[keyof typeof Actions];
|
||||
@@ -46,7 +50,7 @@ export namespace Variants {
|
||||
|
||||
export interface AddedNodeMessage extends MessageBase {
|
||||
action: typeof Actions.AddedNode;
|
||||
payload: [NodeId, NodeDetails, NodeStats, BlockDetails];
|
||||
payload: [NodeId, NodeDetails, NodeStats, BlockDetails, Maybe<NodeLocation>];
|
||||
}
|
||||
|
||||
export interface RemovedNodeMessage extends MessageBase {
|
||||
@@ -54,6 +58,11 @@ export namespace Variants {
|
||||
payload: NodeId;
|
||||
}
|
||||
|
||||
export interface LocatedNodeMessage extends MessageBase {
|
||||
action: typeof Actions.LocatedNode;
|
||||
payload: [NodeId, Latitude, Longitude];
|
||||
}
|
||||
|
||||
export interface ImportedBlockMessage extends MessageBase {
|
||||
action: typeof Actions.ImportedBlock;
|
||||
payload: [NodeId, BlockDetails];
|
||||
@@ -95,6 +104,7 @@ export type Message =
|
||||
| Variants.BestBlockMessage
|
||||
| Variants.AddedNodeMessage
|
||||
| Variants.RemovedNodeMessage
|
||||
| Variants.LocatedNodeMessage
|
||||
| Variants.ImportedBlockMessage
|
||||
| Variants.NodeStatsMessage
|
||||
| Variants.TimeSyncMessage
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './iterators';
|
||||
export * from './helpers';
|
||||
export * from './id';
|
||||
|
||||
@@ -7,4 +6,4 @@ import * as FeedMessage from './feed';
|
||||
|
||||
export { Types, FeedMessage };
|
||||
|
||||
export const VERSION: Types.FeedVersion = 2 as Types.FeedVersion;
|
||||
export const VERSION: Types.FeedVersion = 3 as Types.FeedVersion;
|
||||
@@ -16,7 +16,10 @@ export type PropagationTime = Opaque<Milliseconds, 'PropagationTime'>;
|
||||
export type NodeCount = Opaque<number, 'NodeCount'>;
|
||||
export type PeerCount = Opaque<number, 'PeerCount'>;
|
||||
export type TransactionCount = Opaque<number, 'TransactionCount'>;
|
||||
export type Latitude = Opaque<number, 'Latitude'>;
|
||||
export type Longitude = Opaque<number, 'Longitude'>;
|
||||
|
||||
export type BlockDetails = [BlockNumber, BlockHash, Milliseconds, Timestamp, Maybe<PropagationTime>];
|
||||
export type NodeDetails = [NodeName, NodeImplementation, NodeVersion];
|
||||
export type NodeStats = [PeerCount, TransactionCount];
|
||||
export type NodeLocation = [Latitude, Longitude];
|
||||
@@ -8,7 +8,7 @@
|
||||
<title>Polkadot Telemetry</title>
|
||||
<style>
|
||||
body, html {
|
||||
background: #eee;
|
||||
background: #fff;
|
||||
color: #111;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,7 +50,7 @@ export default class App extends React.Component<{}, State> {
|
||||
<div className="App">
|
||||
<OfflineIndicator status={status} />
|
||||
<Chains chains={chains} subscribed={subscribed} connection={this.connection} />
|
||||
<Chain state={this.state} />
|
||||
<Chain appState={this.state} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ export class Connection {
|
||||
}
|
||||
|
||||
case Actions.AddedNode: {
|
||||
const [id, nodeDetails, nodeStats, blockDetails] = message.payload;
|
||||
const node = { id, nodeDetails, nodeStats, blockDetails };
|
||||
const [id, nodeDetails, nodeStats, blockDetails, location] = message.payload;
|
||||
const node = { id, nodeDetails, nodeStats, blockDetails, location };
|
||||
|
||||
nodes.set(id, node);
|
||||
|
||||
@@ -134,6 +134,19 @@ export class Connection {
|
||||
break;
|
||||
}
|
||||
|
||||
case Actions.LocatedNode: {
|
||||
const [id, latitude, longitude] = message.payload;
|
||||
const node = nodes.get(id);
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.location = [latitude, longitude];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Actions.ImportedBlock: {
|
||||
const [id, blockDetails] = message.payload;
|
||||
const node = nodes.get(id);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1602.7142"
|
||||
height="741.71301"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="Carte du monde simplifiée (à usage pédagogique).svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<inkscape:perspective
|
||||
id="perspective2824"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.43852551"
|
||||
inkscape:cx="625.17606"
|
||||
inkscape:cy="385.25041"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="705"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(275.17606,-309.45844)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 103.03633,1028.2696 c -5.999639,-2.2452 -7.677682,-4.4437 -6.252601,-8.192 0.660208,-1.7365 1.92836,-3.1572 2.818108,-3.1572 0.889763,0 4.784553,2.0468 8.655113,4.5484 3.87055,2.5017 8.60343,5.1172 10.51748,5.8122 l 3.48011,1.2636 -4.28138,0.093 c -2.35476,0.051 -5.8094,0.3148 -7.67697,0.5856 -1.86757,0.2708 -5.13451,-0.1584 -7.25986,-0.9538 z m -13.617114,-11.5249 c -1.129671,-0.6322 -3.013043,-0.845 -4.185257,-0.473 -1.476198,0.4685 -2.616074,-0.2294 -3.708482,-2.2705 -0.86745,-1.6209 -2.422177,-3.2377 -3.454954,-3.5929 -2.369572,-0.815 -7.336604,-8.8624 -10.101315,-16.36571 -1.396299,-3.78947 -3.27412,-6.40899 -5.503286,-7.67697 -3.955704,-2.25003 -4.238878,-3.27049 -1.17721,-4.24223 3.502752,-1.11172 4.154443,-7.28662 1.206028,-11.42728 -1.4072,-1.97624 -2.147696,-4.29669 -1.723727,-5.40156 0.500314,-1.30378 -0.880536,-3.59153 -4.224091,-6.99837 -4.220359,-4.30022 -5.000799,-5.86343 -5.189558,-10.3945 -0.122193,-2.9332 -0.836376,-6.07316 -1.587065,-6.97768 -0.94566,-1.13945 -1.138541,-5.2292 -0.627987,-13.31493 0.553332,-8.76296 0.231029,-14.17132 -1.293746,-21.70944 -2.264363,-11.19452 -3.420974,-32.05213 -2.8326,-51.08135 0.125536,-4.05993 -0.400666,-9.63389 -1.169331,-12.38658 -1.87181,-6.70318 -10.484184,-15.16456 -21.128344,-20.75791 -7.613094,-4.00056 -8.683641,-5.02624 -11.659646,-11.17093 -1.8022915,-3.72126 -5.2467333,-9.28288 -7.6543242,-12.35916 -2.40760275,-3.07628 -5.0252118,-7.59388 -5.8169195,-10.03911 -2.512848,-7.76109 -6.9425399,-15.55931 -9.9621623,-17.53784 -1.60686,-1.05286 -3.439682,-3.79074 -4.072937,-6.08421 -1.02458,-3.71073 -0.754492,-4.74676 2.45272,-9.40827 3.477641,-5.05459 3.518636,-5.26071 1.168493,-5.87529 -5.774696,-1.51012 -3.346071,-9.22866 7.0194155,-22.30875 6.8892381,-8.69344 8.9074651,-15.31624 6.9990541,-22.96735 -0.72726845,-2.91572 -1.37849954,-5.96568 -1.44717885,-6.77766 -0.0687384,-0.81199 -0.58647293,-1.47634 -1.15064725,-1.47634 -0.5641743,0 -0.4556692,-1.06528 0.2411512,-2.36728 1.07365319,-2.00615 0.80246734,-2.5789 -1.7776427,-3.75448 -3.7604139,-1.71335 -9.438416,-0.0195 -9.460348,2.82228 -0.02634,3.40766 -1.524929,3.6587 -6.466202,1.08317 -2.646416,-1.37938 -5.39523,-2.50798 -6.108479,-2.50798 -0.713261,0 -3.487173,-1.88687 -6.164261,-4.19303 -2.677088,-2.30617 -5.531785,-4.71879 -6.343772,-5.36139 -0.811986,-0.6426 -1.476339,-2.39374 -1.476339,-3.89142 0,-1.49768 -1.042957,-3.66692 -2.317676,-4.82053 -1.274731,-1.15361 -2.926542,-3.94232 -3.6707,-6.19714 -0.978057,-2.96353 -1.710463,-3.74223 -2.642825,-2.80987 -1.945768,1.94577 -4.82665,1.57622 -11.818936,-1.51609 -3.655441,-1.61662 -7.463676,-4.37747 -8.984826,-6.51372 -1.452104,-2.0393 -4.226453,-4.5281 -6.165217,-5.53067 -3.199405,-1.65448 -3.999345,-1.64172 -8.659877,0.13816 l -5.134851,1.96101 -15.754258,-7.63407 c -9.69082,-4.6959 -17.21178,-9.17543 -19.54129,-11.63894 -3.6119,-3.81967 -3.72877,-4.23446 -2.52692,-8.96936 1.10099,-4.33758 0.91403,-5.67948 -1.48062,-10.62649 -1.50741,-3.1141 -4.80097,-7.89722 -7.31902,-10.62914 -2.51805,-2.73191 -4.29226,-5.42992 -3.94267,-5.99555 0.34957,-0.56562 -1.42418,-4.37776 -3.94167,-8.47141 -2.85994,-4.65052 -4.87833,-9.6321 -5.37973,-13.27775 -0.64275,-4.67339 -1.39167,-6.10321 -3.76259,-7.18347 -2.51322,-1.1451 -3.21598,-0.99916 -4.655,0.96669 -2.02672,2.76872 -1.45057,5.69173 3.03076,15.37588 1.82106,3.93532 3.70302,9.76916 4.18213,12.96407 0.47911,3.19492 1.89425,7.81448 3.14476,10.26569 2.96198,5.80595 1.04946,5.70351 -3.62143,-0.19397 -2.75488,-3.47831 -3.49889,-5.38594 -2.95142,-7.5673 0.58456,-2.32906 0.009,-3.4507 -2.85428,-5.56806 -4.34257,-3.21059 -5.26164,-5.2817 -3.66648,-8.26228 0.93021,-1.73812 0.50367,-3.64427 -1.85868,-8.30615 -2.39904,-4.73427 -2.90082,-7.02763 -2.30685,-10.54332 0.97096,-5.74708 -1.60746,-10.94864 -6.41421,-12.93966 -2.77525,-1.14955 -3.73197,-2.43553 -4.29743,-5.77645 -0.40007,-2.36374 -1.1663,-6.02083 -1.70272,-8.12689 -0.55799,-2.19071 -0.5174,-4.11222 0.0949,-4.49061 0.5886,-0.36377 0.20347,-1.98417 -0.85584,-3.60088 -1.56016,-2.38111 -1.66699,-3.50796 -0.5624,-5.93227 0.74999,-1.64604 1.03306,-3.52765 0.62905,-4.18135 -0.40401,-0.65369 0.60277,-2.60692 2.23728,-4.34051 1.63451,-1.73359 3.8106,-5.19102 4.83574,-7.68319 1.02515,-2.49217 4.18431,-7.11207 7.02037,-10.26644 2.83605,-3.15439 6.43146,-8.67989 7.9898,-12.27891 2.16305,-4.99564 3.43094,-6.54843 5.35951,-6.56382 1.3894,-0.0106 2.45237,0.6442 2.36215,1.45618 -0.37598,3.38371 2.29955,2.34125 5.30474,-2.06687 1.77141,-2.59836 2.72717,-4.90145 2.12392,-5.11798 -0.60324,-0.21653 -1.09681,-1.19402 -1.09681,-2.17222 0,-0.97819 -2.44505,-4.15485 -5.43343,-7.05922 -4.95654,-4.81722 -5.27485,-5.45592 -3.6267,-7.27709 1.33529,-1.47549 1.49233,-2.55818 0.60183,-4.14941 -1.56493,-2.79638 0.90447,-9.05861 3.58203,-9.08378 1.1059,-0.0106 1.42152,-0.38694 0.74251,-0.8858 -1.52334,-1.11919 -2.2135,-3.66087 -1.99948,-7.36362 0.34182,-5.91393 -0.95236,-8.38769 -5.3492,-10.2248 -2.37157,-0.99091 -5.99032,-3.08181 -8.04168,-4.64646 -2.05136,-1.56465 -4.65399,-2.8448Line truncated
|
||||
id="path2830" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="265.7" width="494.7" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke-linejoin="bevel" stroke-width="0.25" stroke="#000">
|
||||
<use stroke-width="1.5" xlink:href="#a"/>
|
||||
<g id="a" fill="#bcbec0"><path d="m275.9 201.8-1.4 1.46h-2.36v-2.15l1.83-1.18h1.93v1.87zm4.05-6.3v2.12l-1.8-1.08v-1.65h1.8v-5.97h-4.6l-10.94 7.33-2.69-3.09v6.07h-6.65l3.86 10.57h12.67l9.75-11.28 0.44-3.05-1.84 0.03zm-171.6-119.4 2.52-1.719h3.04l-2.51 1.719h-3.05zm22.97-9.127-3.22 2.792h-7.46l-2.31 1.582h1.78l-2.48 1.692h-1.77l2.47-1.692h-4.87l-2.11 1.64-2.15 0.215 0.43-3.705h-1.66l-1.94 1.289-3.6 5.315h-2.12l4.08-6.604 4.81-1.029-0.27-0.903-3.17-0.108-1.5-0.913-1.88 0.913h-2.2l0.9-1.945-4.551-2.082h-38.81l-12.78 13.48v10.36l4.666 4.667 16.32 2.098 2.177 3.302 4.59-0.161 3.788 7.44 7.058-8.861 16.78 0.053 1.11 8.388 2.66 0.07 0.07-10.44 27.16-23.22 1.85-0.32 1.05-3.307h-2.9zm16.06-1.435h9.48l-1.97-3.138-1.78-0.425v-2.251zm-70.28 50.76-3.657-7.88 3.036-3.81-3.787-7.442-4.59 0.161-2.177-3.301-16.32-2.098 0.031 0.032 0.695 7.348 5.02 9.5 2.057-1.59-3.959-8.441v-4.751h2.335l0.091 4.419 7.5 17.62 12.23 5.1 3.052-0.81 2.686 2.74 1.338-2.38h2.818l-0.402-3.09h2.415l3.141-2.34 0.133-3.41-3.435-0.06-2.731 4.48zm135.4-76.39 4.54-2.209v-0.622l-2.25-2.282-3.15 1.046-3.78-0.08-0.8 1.09-2.15-0.05v2.02zm40.41 47.95 0.01-2.144-2.43-0.064-0.02 1.58zm-92.08-68.56v2.26l11.28 0.161 2.89 4.987v4.51l-5.41 3.073 0.16 6.12 3.4 3.437h4.02l6.14-6.489 8.67-2.557 3.25-2.489 6.56-0.405 4.59-1.355 8.65-13.75 5.33-1.09 1.88-1.89-15.37-1.546-13.28 1.546h-16.08l-9.27 3.221zm261.6 87.05-1.31-1.37-2.15 3.06 2.15 2.01zm-20.64-37.93 0.43-2.416-4.51-0.725-1.72-2.819h-8.59l-17.56-4.348-1.93 4.348-7.14-2.174-5.33 4.06 17.36 10.14 20.08 0.215 2.42-4.924zm3.81 45.98-2.58 0.57v1.52l1.05 1.27 1.53-1.91zm-9.61-51.94 1.72 2.819 4.51 0.725-0.43 2.416-6.48 1.357-2.43 4.924-20.08-0.215-17.36-10.14-7.51 5.725 1.25 5.811-7.52 2.793 2.15 5.26 8 3.65 0.32 6.443 20.94 7.242 3.86-3.538 8.22 3.538-1.13 3.06 4.35 7.25 7.62-3.86 4.98 4.26 2.11-1.2 0.08 2.05 12.48-4.46 4.08-9.132-7.28-11.98 3.74-4.079h-4.3l-1.38 0.893-2.7-4.435 3.44-3.007 2.36 4.504 3.31-2.277 6.35-4.374v-8.483l-10.52-2.684-7.73-7.571h-7.51l1.71 6.067zm43.39 12.76 2.94-0.04 1.25-2.134-6.53-2.738 0.13 5.395 1.29 0.765zm-8.12 12.47-0.48 1.659 6.47-0.955 0.5 2.498 1.47-0.624 0.16-1.966 4.34-1.115-0.23-5.673-1.97-3.439-1.96-0.568 1.12 5.154-4.01 3.637zm-1.14 3.202 2.34-0.295 0.76 1.128-0.84 2.657zm6.24-1.1-1.13 2.053-1.49-1.691zm-7.08 35.04-3.55-0.37-1.29-1.61 1.29-2.96-1.28-2.84-2.34-0.05-0.57 5 3.31 3.99 3.88 0.49zm-11.18 8.87 1.57 0.31 2.79-2.32-0.06-1.9zm12.25 3.59v2.23l2.33-0.7-0.79-1.44 1.88-0.48-1.6-4.99-1.18 3.08h-3.86zm-6.87-11.13 1.66 1.63-0.54 1-1.34-1.31zm1.66 3.99 1.18 0.8 1.29-1.95-1.72-0.21zm4.4-2.04 1.62 2.09h1.77l-1.69-2.72zm0 1.76 0.91 1.59-0.73 0.97-1.28-1.93zm-1.93 1.61 0.83 1.25-0.83 1-0.95-1.16zm3.15 32.76 1.09 1.5 3.06-0.37v-1.13zm-24.59-17.64 0.15 4.32 2.46 4.3h7.82l3.87-4.94v-8.96l-2.79 0.29-2.26 4.27-7.57 1.69-1.63-0.99zm-5.56 11.28v-3.3l-12.89-13.37-4.41-1.69 14.93 20.33zm15.06 5.88 0.24 1.12-16.11-2.73 2.42-1.94 4.02 1.53 3.63-0.8zm10.3 0h-2.49l0.48 1.61h2.38l1-0.81zm-4.35 2.61 0.73 0.89h1.73l-0.6-1.29zm9.14-2.13-2.49 2.78 1.89 0.24 1.69-1.52zm-8.29-4.91 1.85-0.57-0.56-3.26 1.09-1.21 0.92 2.42 2.22 0.77v-1.74l-1.25-2.76 2.37-1.99-3.78 0.16-0.78-1.08 1.5-1.65 2.78 0.96 1.37-0.12 2.05-2.42-1.8-0.04-1.17 0.77-3.87-0.94-2.56 2.31 0.18 3.66-1.49 1.98zm13.89-5.19-0.43 1.51 4.19-0.43v-1.08zm0.97-9.75-1.4 1.38-0.53 3.44 1.93 0.47-0.43-3.05 1.12-1.4zm9.49 9.15-0.56-2.98-4.59 0.65zm-0.31 2.48 6.4 2.67 1.15 3.27-2.13 1.3 5.48 1.9v-10.79l-4.45-2.32-4.15 3.37-4.39-1.73-0.49 2.42zm-19.06-71.18-4.32-3.535-1.91 1.764 2.58 5.584 3.12-0.215zm-4.32-3.535-1.91-1.564 2.47-0.322-0.43-4.939-6.35 4.374 1.86 3.53 2.45 0.685zm-5.53 56.98-3.44 4.63-7.94 3.67 1.63 0.99 7.57-1.69 2.26-4.27 2.79-0.29v-3.28zm-26.32 0 1.38 4.48 5.68 4.84 0.44-1.72-5.36-7.92zm68.12 27.58 3.81-3.5 4.94 4.82 4.51 0.92-5.58-5.58 0.27-2.95-9.94-5.2v10.79zm10.46-5.78 2.01 1.2 3.47-1.95v-2.02zm2.8-5.67 4.05 2.9 1.62-0.55-4.11-2.45zm-84.29-42.84-4.35-7.25 1.12-3.06-2.52-1.08-3.38 2.43-1.59 10.99 2.51 2.1 0.96 5.02 1.77 0.53 1.27-1.59 2.36 2.66 1.31 4.86-0.31 5.02 2.03-3.98-3.65-13.16 2.47-1.79zm7.3 6.18 2.92 3.97-2.73 2.74h6.12v-1.56l-5.96-6.34-1.32-4.08-4.14-2.02-2.19 1.11v1.7l2.14 1.25v3.23zm-5.16 0v-3.23l-2.14-1.25-2.47 1.79 3.65 13.16-2.03 3.98-0.12 1.98 3.54 3 0.34 1.1 2.14-0.32-3.44-5.07 0.8-7.84 3.87 3.83v-3.21l3.94-3.95-2.92-3.97zm4.14 7.92v3.21l1.93 1.92 2.17-0.3 3.23-2.42v-3.62h-6.12zm1.37-9.11 5.96 6.34v5.18l-3.23 2.42 0.08 3.46 6.18-4.08-0.21-6.07-6.45-6.81 0.32-3.73 2.3-1.3-4.98-4.26-5.43 2.75 4.14 2.02zm-25.77-13.2-1.37 0.64 1.94 6.13 1.25-1.21 5.58 4.68 0.36-2.5-2.93-2.74-0.48-3.28-2.34 0.96zm2.01-0.42-3.38-0.84 0.47-2.12-0.97-0.336-2.07 3.296-11.37-4.306 0.03-3.632-2.84-0.984-0.32-6.443-2.48-1.13h-2.29l-3.4 2.416-0.09 2.057 2.06 1.167 0.98 4.021-4.47 4.114h-2.86v3.04l2.14 0.81v2.62l-3.86 1.03 0.33 0.39 3.19 1.01-1.49 1.53 1.41 1.83 2.53 0.3 0.45-2.1 1 0.01 8.13 23.38 5.16-3.17 0.8-9.4 11.77-11.37-1.94-6.13 1.37-0.64 2.01 2.68 2.34-0.96 0.48 3.28 2.93 2.74 1.23-8.49 3.38-2.43-5.69-2.458-3.86 3.538zm-17.29-7.938-0.03 Line truncated
|
||||
</g></g></svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -8,12 +8,35 @@
|
||||
|
||||
.Chain-content-container {
|
||||
position: absolute;
|
||||
left: 0; /*80px;*/
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 148px;
|
||||
}
|
||||
|
||||
.Chain-map {
|
||||
background: url('../assets/world-map.svg') no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.Chain-map-node {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #f00; /* #d64ca8;*/
|
||||
border-radius: 5px;
|
||||
margin-left: -5px;
|
||||
margin-top: -5px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.Chain-content {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { State } from '../state';
|
||||
import { State as AppState } from '../state';
|
||||
import { formatNumber } from '../utils';
|
||||
import { Tile, Icon, Node, Ago } from './';
|
||||
|
||||
@@ -17,7 +17,11 @@ import './Chain.css';
|
||||
|
||||
export namespace Chain {
|
||||
export interface Props {
|
||||
state: Readonly<State>
|
||||
appState: Readonly<AppState>;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
display: 'map' | 'table';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,42 +38,75 @@ function sortNodes(a: Node.Props, b: Node.Props): number {
|
||||
return aPropagation - bPropagation;
|
||||
}
|
||||
|
||||
export function Chain(props: Chain.Props) {
|
||||
const { best, blockTimestamp, blockAverage } = props.state;
|
||||
export class Chain extends React.Component<Chain.Props, Chain.State> {
|
||||
constructor(props: Chain.Props) {
|
||||
super(props);
|
||||
|
||||
const nodes = Array.from(props.state.nodes.values()).sort(sortNodes);
|
||||
this.state = {
|
||||
display: 'table'
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Chain">
|
||||
<div className="Chain-header">
|
||||
<Tile icon={blockIcon} title="Best Block">#{formatNumber(best)}</Tile>
|
||||
<Tile icon={blockTimeIcon} title="Avgerage Time">{ blockAverage == null ? '-' : (blockAverage / 1000).toFixed(3) + 's' }</Tile>
|
||||
<Tile icon={lastTimeIcon} title="Last Block"><Ago when={blockTimestamp} /></Tile>
|
||||
</div>
|
||||
<div className="Chain-content-container">
|
||||
<div className="Chain-content">
|
||||
<table className="Chain-node-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><Icon src={nodeIcon} alt="Node" /></th>
|
||||
<th><Icon src={nodeTypeIcon} alt="Implementation" /></th>
|
||||
<th><Icon src={peersIcon} alt="Peer Count" /></th>
|
||||
<th><Icon src={transactionsIcon} alt="Transactions in Queue" /></th>
|
||||
<th><Icon src={blockIcon} alt="Block" /></th>
|
||||
<th><Icon src={blockHashIcon} alt="Block Hash" /></th>
|
||||
<th><Icon src={blockTimeIcon} alt="Block Time" /></th>
|
||||
<th><Icon src={propagationTimeIcon} alt="Block Propagation Time" /></th>
|
||||
<th><Icon src={lastTimeIcon} alt="Last Block Time" /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
nodes.map((node) => <Node key={node.id} {...node} />)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
public render() {
|
||||
const { best, blockTimestamp, blockAverage } = this.props.appState;
|
||||
|
||||
return (
|
||||
<div className="Chain">
|
||||
<div className="Chain-header">
|
||||
<Tile icon={blockIcon} title="Best Block">#{formatNumber(best)}</Tile>
|
||||
<Tile icon={blockTimeIcon} title="Avgerage Time">{ blockAverage == null ? '-' : (blockAverage / 1000).toFixed(3) + 's' }</Tile>
|
||||
<Tile icon={lastTimeIcon} title="Last Block"><Ago when={blockTimestamp} /></Tile>
|
||||
</div>
|
||||
<div className="Chain-content-container">
|
||||
<div className="Chain-content">
|
||||
{
|
||||
this.state.display === 'table'
|
||||
? this.renderTable()
|
||||
: this.renderMap()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private renderMap() {
|
||||
// return <ReactSVG path={worldMap} className="Chain-map" />;
|
||||
return (
|
||||
<div className="Chain-map">
|
||||
{
|
||||
this.nodes().map((node) => <div key={node.id} className="Chain-map-node" data-foo={JSON.stringify(node)} />)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderTable() {
|
||||
return (
|
||||
<table className="Chain-node-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><Icon src={nodeIcon} alt="Node" /></th>
|
||||
<th><Icon src={nodeTypeIcon} alt="Implementation" /></th>
|
||||
<th><Icon src={peersIcon} alt="Peer Count" /></th>
|
||||
<th><Icon src={transactionsIcon} alt="Transactions in Queue" /></th>
|
||||
<th><Icon src={blockIcon} alt="Block" /></th>
|
||||
<th><Icon src={blockHashIcon} alt="Block Hash" /></th>
|
||||
<th><Icon src={blockTimeIcon} alt="Block Time" /></th>
|
||||
<th><Icon src={propagationTimeIcon} alt="Block Propagation Time" /></th>
|
||||
<th><Icon src={lastTimeIcon} alt="Last Block Time" /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
this.nodes().sort(sortNodes).map((node) => <Node key={node.id} {...node} />)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
private nodes() {
|
||||
return Array.from(this.props.appState.nodes.values());
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
margin: 0;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background: #fff;
|
||||
border: 2px solid #fff;
|
||||
background: #3c3c3b;
|
||||
border: 2px solid #3c3c3b;
|
||||
border-radius: 24px;
|
||||
color: #555;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.Chains-node-count {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { formatNumber, trimHash } from '../utils';
|
||||
import { Ago } from './Ago';
|
||||
import { Types } from '@dotstats/common';
|
||||
import { Types, Maybe } from '@dotstats/common';
|
||||
|
||||
export namespace Node {
|
||||
export interface Props {
|
||||
@@ -9,6 +9,7 @@ export namespace Node {
|
||||
nodeDetails: Types.NodeDetails,
|
||||
nodeStats: Types.NodeStats,
|
||||
blockDetails: Types.BlockDetails,
|
||||
location: Maybe<Types.NodeLocation>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user