Update frontend dependencies (#498)

* update npm packages

* update tsconfig

* remove babelrc, babel presets as well as stable package

* fix svg namespace tag syntax errors

* fix reference error due to namespace and class component having same name

* replace tslint with eslint

* make eslint happier

* update .nvmrc to 14

* update node version to 14 in gh workflow

* fix eslint warnings due to warnings treated as errors on CI (process.env.CI = true)

* pretty fix

* bump node version in Dockerfile

* use createRoot instead of react-dom render

* update browsers list in package.json
This commit is contained in:
serge
2022-09-16 10:07:51 +00:00
committed by GitHub
parent d525056190
commit 41c93a8a19
72 changed files with 8089 additions and 8612 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
strategy:
matrix:
node-version: [10.x, 12.x]
node-version: [14.x]
steps:
- uses: actions/checkout@v3
-3
View File
@@ -1,3 +0,0 @@
{
"presets": ["env", "react"]
}
+40
View File
@@ -0,0 +1,40 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/jsx-runtime",
"plugin:react/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["react", "react-hooks", "@typescript-eslint"],
"rules": {
"no-console": 0,
"no-empty": 0,
"no-bitwise": 0,
"quotes": [1, "single"],
"jsx-quotes": [1, "prefer-double"],
"semi": [1, "always"],
"@typescript-eslint/prefer-as-const": 1,
"@typescript-eslint/no-namespace": 1,
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
]
}
}
+1 -1
View File
@@ -1 +1 @@
12
14
+1 -1
View File
@@ -1,5 +1,5 @@
#### BUILDER IMAGE ####
FROM docker.io/node:12 as builder
FROM docker.io/node:14 as builder
LABEL maintainer="Chevdor <chevdor@gmail.com>"
LABEL description="Substrate Telemetry Frontend builder image"
+36 -32
View File
@@ -6,44 +6,45 @@
"description": "Polkadot Telemetry frontend",
"scripts": {
"precommit": "lint-staged",
"start": "react-scripts-ts start",
"build": "react-scripts-ts build",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "TS_NODE_PROJECT=tsconfig.test.json tape -r ts-node/register test/**/*spec.ts",
"test:coverage": "nyc yarn test",
"eject": "react-scripts-ts eject",
"eject": "react-scripts eject",
"pretty:check": "prettier --check src/**/*.{ts,tsx}",
"pretty:fix": "prettier --write src",
"clean": "rm -rf node_modules build .nyc ./tmp/env-config.js report*.json yarn-error.log"
"clean": "rm -rf node_modules build .nyc ./tmp/env-config.js report*.json yarn-error.log",
"lint": "eslint src"
},
"dependencies": {
"@polkadot/util-crypto": "^2.8.1",
"@polkadot/util-crypto": "^10.1.7",
"@types/react-measure": "^2.0.6",
"blakejs": "^1.1.0",
"husky": "^4.2.5",
"lint-staged": "^10.1.7",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-measure": "^2.3.0",
"react-scripts-ts": "3.1.0",
"stable": "^0.1.8",
"tslint": "^6.1.1"
"blakejs": "^1.2.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-measure": "^2.5.2",
"react-scripts": "5.0.1"
},
"devDependencies": {
"@types/node": "^13.13.2",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"@types/node": "^16.11.58",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"@types/tape": "^4.13.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"eslint": "^8.23.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"identity-obj-proxy": "^3.0.0",
"istanbul": "^0.4.5",
"nyc": "^15.0.1",
"prettier": "^2.0.5",
"tape": "^4.13.2",
"ts-node": "^8.9.0",
"tslint": "^6.1.1",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.3.0",
"typescript": "^4.4.2"
},
"lint-staged": {
@@ -51,15 +52,18 @@
"pretty:fix"
]
},
"resolutions": {
"chalk": "^2.4.2",
"node-forge": ">=0.10.0",
"yargs-parser": ">=13.1.2",
"dot-prop": ">=4.2.1",
"lodash": ">=4.17.19",
"serialize-javascript": ">=3.1.0",
"js-yaml": ">=3.13.1",
"webpack-dev-server": "^2.11.4",
"mem": ">=4.0.0"
"browserslist": {
"production": [
"chrome >= 67",
"edge >= 79",
"firefox >= 68",
"opera >= 54",
"safari >= 14"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
+6 -7
View File
@@ -27,23 +27,23 @@ import {
Node,
ChainData,
comparePinnedChains,
StateSettings,
} from './state';
import { getHashData } from './utils';
import stable from 'stable';
import './App.css';
export default class App extends React.Component<{}, {}> {
export default class App extends React.Component {
private chainsCache: ChainData[] = [];
// Custom state for finer control over updates
private readonly appState: Readonly<State>;
private readonly appUpdate: Update;
private readonly settings: PersistentObject<State.Settings>;
private readonly settings: PersistentObject<StateSettings>;
private readonly pins: PersistentSet<Types.NodeName>;
private readonly sortBy: Persistent<Maybe<number>>;
private readonly connection: Promise<Connection>;
constructor(props: {}) {
constructor(props: Record<string, unknown>) {
super(props);
this.settings = new PersistentObject(
@@ -225,8 +225,7 @@ export default class App extends React.Component<{}, {}> {
return this.chainsCache;
}
this.chainsCache = stable.inplace(
Array.from(this.appState.chains.values()),
this.chainsCache = Array.from(this.appState.chains.values()).sort(
(a, b) => {
const pinned = comparePinnedChains(a.genesisHash, b.genesisHash);
@@ -241,7 +240,7 @@ export default class App extends React.Component<{}, {}> {
return this.chainsCache;
}
private selectedColumns(settings: State.Settings): Column[] {
private selectedColumns(settings: StateSettings): Column[] {
return Row.columns.filter(
({ setting }) => setting == null || settings[setting]
);
+6 -4
View File
@@ -68,7 +68,7 @@ export class Connection {
return `wss://${window.location.hostname}/feed/`;
}
return `ws://127.0.0.1:8000/feed`;
return 'ws://127.0.0.1:8000/feed';
}
private static async socket(): Promise<WebSocket> {
@@ -89,7 +89,7 @@ export class Connection {
}
private static async trySocket(): Promise<Maybe<WebSocket>> {
return new Promise<Maybe<WebSocket>>((resolve, _) => {
return new Promise<Maybe<WebSocket>>((resolve) => {
function clean() {
socket.removeEventListener('open', onSuccess);
socket.removeEventListener('close', onFailure);
@@ -469,7 +469,8 @@ export class Connection {
let data: FeedMessage.Data;
if (typeof event.data === 'string') {
data = (event.data as any) as FeedMessage.Data;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data = event.data as any as FeedMessage.Data;
} else {
const u8aData = new Uint8Array(event.data);
@@ -480,7 +481,8 @@ export class Connection {
const str = Connection.utf8decoder.decode(event.data);
data = (str as any) as FeedMessage.Data;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data = str as any as FeedMessage.Data;
}
this.handleMessages(FeedMessage.deserialize(data));
+4 -6
View File
@@ -109,9 +109,7 @@ export function sortedIndexOf<T>(
return -1;
}
export namespace SortedCollection {
export type StateRef = Opaque<number, 'SortedCollection.StateRef'>;
}
type StateRef = Opaque<number, 'SortedCollection.StateRef'>;
interface Focus {
start: number;
@@ -268,12 +266,12 @@ export class SortedCollection<Item extends { id: number }> {
}
// Get the reference to current ordering state of focused items.
public get ref(): SortedCollection.StateRef {
return this.changeRef as SortedCollection.StateRef;
public get ref(): StateRef {
return this.changeRef as StateRef;
}
// Check if order of focused items has changed since obtaining a `ref`.
public hasChangedSince(ref: SortedCollection.StateRef): boolean {
public hasChangedSince(ref: StateRef): boolean {
return this.changeRef > ref;
}
}
+150 -152
View File
@@ -41,194 +41,192 @@ import {
} from './types';
export const ACTIONS = {
FeedVersion: 0x00 as 0x00,
BestBlock: 0x01 as 0x01,
BestFinalized: 0x02 as 0x02,
AddedNode: 0x03 as 0x03,
RemovedNode: 0x04 as 0x04,
LocatedNode: 0x05 as 0x05,
ImportedBlock: 0x06 as 0x06,
FinalizedBlock: 0x07 as 0x07,
NodeStats: 0x08 as 0x08,
NodeHardware: 0x09 as 0x09,
TimeSync: 0x0a as 0x0a,
AddedChain: 0x0b as 0x0b,
RemovedChain: 0x0c as 0x0c,
SubscribedTo: 0x0d as 0x0d,
UnsubscribedFrom: 0x0e as 0x0e,
Pong: 0x0f as 0x0f,
AfgFinalized: 0x10 as 0x10,
AfgReceivedPrevote: 0x11 as 0x11,
AfgReceivedPrecommit: 0x12 as 0x12,
AfgAuthoritySet: 0x13 as 0x13,
StaleNode: 0x14 as 0x14,
NodeIO: 0x15 as 0x15,
ChainStatsUpdate: 0x16 as 0x16,
FeedVersion: 0x00 as const,
BestBlock: 0x01 as const,
BestFinalized: 0x02 as const,
AddedNode: 0x03 as const,
RemovedNode: 0x04 as const,
LocatedNode: 0x05 as const,
ImportedBlock: 0x06 as const,
FinalizedBlock: 0x07 as const,
NodeStats: 0x08 as const,
NodeHardware: 0x09 as const,
TimeSync: 0x0a as const,
AddedChain: 0x0b as const,
RemovedChain: 0x0c as const,
SubscribedTo: 0x0d as const,
UnsubscribedFrom: 0x0e as const,
Pong: 0x0f as const,
AfgFinalized: 0x10 as const,
AfgReceivedPrevote: 0x11 as const,
AfgReceivedPrecommit: 0x12 as const,
AfgAuthoritySet: 0x13 as const,
StaleNode: 0x14 as const,
NodeIO: 0x15 as const,
ChainStatsUpdate: 0x16 as const,
};
export type Action = typeof ACTIONS[keyof typeof ACTIONS];
export type Payload = Message['payload'];
export namespace Variants {
export interface MessageBase {
action: Action;
}
interface MessageBase {
action: Action;
}
export interface FeedVersionMessage extends MessageBase {
action: typeof ACTIONS.FeedVersion;
payload: FeedVersion;
}
interface FeedVersionMessage extends MessageBase {
action: typeof ACTIONS.FeedVersion;
payload: FeedVersion;
}
export interface BestBlockMessage extends MessageBase {
action: typeof ACTIONS.BestBlock;
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
}
interface BestBlockMessage extends MessageBase {
action: typeof ACTIONS.BestBlock;
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
}
export interface BestFinalizedBlockMessage extends MessageBase {
action: typeof ACTIONS.BestFinalized;
payload: [BlockNumber, BlockHash];
}
interface BestFinalizedBlockMessage extends MessageBase {
action: typeof ACTIONS.BestFinalized;
payload: [BlockNumber, BlockHash];
}
export interface AddedNodeMessage extends MessageBase {
action: typeof ACTIONS.AddedNode;
payload: [
NodeId,
NodeDetails,
NodeStats,
NodeIO,
NodeHardware,
BlockDetails,
Maybe<NodeLocation>,
Maybe<Timestamp>
];
}
interface AddedNodeMessage extends MessageBase {
action: typeof ACTIONS.AddedNode;
payload: [
NodeId,
NodeDetails,
NodeStats,
NodeIO,
NodeHardware,
BlockDetails,
Maybe<NodeLocation>,
Maybe<Timestamp>
];
}
export interface RemovedNodeMessage extends MessageBase {
action: typeof ACTIONS.RemovedNode;
payload: NodeId;
}
interface RemovedNodeMessage extends MessageBase {
action: typeof ACTIONS.RemovedNode;
payload: NodeId;
}
export interface LocatedNodeMessage extends MessageBase {
action: typeof ACTIONS.LocatedNode;
payload: [NodeId, Latitude, Longitude, City];
}
interface LocatedNodeMessage extends MessageBase {
action: typeof ACTIONS.LocatedNode;
payload: [NodeId, Latitude, Longitude, City];
}
export interface ImportedBlockMessage extends MessageBase {
action: typeof ACTIONS.ImportedBlock;
payload: [NodeId, BlockDetails];
}
interface ImportedBlockMessage extends MessageBase {
action: typeof ACTIONS.ImportedBlock;
payload: [NodeId, BlockDetails];
}
export interface FinalizedBlockMessage extends MessageBase {
action: typeof ACTIONS.FinalizedBlock;
payload: [NodeId, BlockNumber, BlockHash];
}
interface FinalizedBlockMessage extends MessageBase {
action: typeof ACTIONS.FinalizedBlock;
payload: [NodeId, BlockNumber, BlockHash];
}
export interface NodeStatsMessage extends MessageBase {
action: typeof ACTIONS.NodeStats;
payload: [NodeId, NodeStats];
}
interface NodeStatsMessage extends MessageBase {
action: typeof ACTIONS.NodeStats;
payload: [NodeId, NodeStats];
}
export interface NodeHardwareMessage extends MessageBase {
action: typeof ACTIONS.NodeHardware;
payload: [NodeId, NodeHardware];
}
interface NodeHardwareMessage extends MessageBase {
action: typeof ACTIONS.NodeHardware;
payload: [NodeId, NodeHardware];
}
export interface NodeIOMessage extends MessageBase {
action: typeof ACTIONS.NodeIO;
payload: [NodeId, NodeIO];
}
interface NodeIOMessage extends MessageBase {
action: typeof ACTIONS.NodeIO;
payload: [NodeId, NodeIO];
}
export interface TimeSyncMessage extends MessageBase {
action: typeof ACTIONS.TimeSync;
payload: Timestamp;
}
interface TimeSyncMessage extends MessageBase {
action: typeof ACTIONS.TimeSync;
payload: Timestamp;
}
export interface AddedChainMessage extends MessageBase {
action: typeof ACTIONS.AddedChain;
payload: [ChainLabel, GenesisHash, NodeCount];
}
interface AddedChainMessage extends MessageBase {
action: typeof ACTIONS.AddedChain;
payload: [ChainLabel, GenesisHash, NodeCount];
}
export interface RemovedChainMessage extends MessageBase {
action: typeof ACTIONS.RemovedChain;
payload: GenesisHash;
}
interface RemovedChainMessage extends MessageBase {
action: typeof ACTIONS.RemovedChain;
payload: GenesisHash;
}
export interface SubscribedToMessage extends MessageBase {
action: typeof ACTIONS.SubscribedTo;
payload: GenesisHash;
}
interface SubscribedToMessage extends MessageBase {
action: typeof ACTIONS.SubscribedTo;
payload: GenesisHash;
}
export interface UnsubscribedFromMessage extends MessageBase {
action: typeof ACTIONS.UnsubscribedFrom;
payload: GenesisHash;
}
interface UnsubscribedFromMessage extends MessageBase {
action: typeof ACTIONS.UnsubscribedFrom;
payload: GenesisHash;
}
export interface PongMessage extends MessageBase {
action: typeof ACTIONS.Pong;
payload: string; // just echo whatever `ping` sent
}
interface PongMessage extends MessageBase {
action: typeof ACTIONS.Pong;
payload: string; // just echo whatever `ping` sent
}
export interface AfgFinalizedMessage extends MessageBase {
action: typeof ACTIONS.AfgFinalized;
payload: [Address, BlockNumber, BlockHash];
}
interface AfgFinalizedMessage extends MessageBase {
action: typeof ACTIONS.AfgFinalized;
payload: [Address, BlockNumber, BlockHash];
}
export interface AfgAuthoritySet extends MessageBase {
action: typeof ACTIONS.AfgAuthoritySet;
payload: AuthoritySetInfo;
}
interface AfgAuthoritySet extends MessageBase {
action: typeof ACTIONS.AfgAuthoritySet;
payload: AuthoritySetInfo;
}
export interface AfgReceivedPrecommit extends MessageBase {
action: typeof ACTIONS.AfgReceivedPrecommit;
payload: [Address, BlockNumber, BlockHash, Address];
}
interface AfgReceivedPrecommit extends MessageBase {
action: typeof ACTIONS.AfgReceivedPrecommit;
payload: [Address, BlockNumber, BlockHash, Address];
}
export interface AfgReceivedPrevote extends MessageBase {
action: typeof ACTIONS.AfgReceivedPrevote;
payload: [Address, BlockNumber, BlockHash, Address];
}
interface AfgReceivedPrevote extends MessageBase {
action: typeof ACTIONS.AfgReceivedPrevote;
payload: [Address, BlockNumber, BlockHash, Address];
}
export interface StaleNodeMessage extends MessageBase {
action: typeof ACTIONS.StaleNode;
payload: NodeId;
}
interface StaleNodeMessage extends MessageBase {
action: typeof ACTIONS.StaleNode;
payload: NodeId;
}
export interface ChainStatsUpdate extends MessageBase {
action: typeof ACTIONS.ChainStatsUpdate;
payload: ChainStats;
}
interface ChainStatsUpdate extends MessageBase {
action: typeof ACTIONS.ChainStatsUpdate;
payload: ChainStats;
}
export type Message =
| Variants.FeedVersionMessage
| Variants.BestBlockMessage
| Variants.BestFinalizedBlockMessage
| Variants.AddedNodeMessage
| Variants.RemovedNodeMessage
| Variants.LocatedNodeMessage
| Variants.ImportedBlockMessage
| Variants.FinalizedBlockMessage
| Variants.NodeStatsMessage
| Variants.NodeHardwareMessage
| Variants.TimeSyncMessage
| Variants.AddedChainMessage
| Variants.RemovedChainMessage
| Variants.SubscribedToMessage
| Variants.UnsubscribedFromMessage
| Variants.AfgFinalizedMessage
| Variants.AfgReceivedPrevote
| Variants.AfgReceivedPrecommit
| Variants.AfgAuthoritySet
| Variants.StaleNodeMessage
| Variants.PongMessage
| Variants.NodeIOMessage
| Variants.ChainStatsUpdate;
| FeedVersionMessage
| BestBlockMessage
| BestFinalizedBlockMessage
| AddedNodeMessage
| RemovedNodeMessage
| LocatedNodeMessage
| ImportedBlockMessage
| FinalizedBlockMessage
| NodeStatsMessage
| NodeHardwareMessage
| TimeSyncMessage
| AddedChainMessage
| RemovedChainMessage
| SubscribedToMessage
| UnsubscribedFromMessage
| AfgFinalizedMessage
| AfgReceivedPrevote
| AfgReceivedPrecommit
| AfgAuthoritySet
| StaleNodeMessage
| PongMessage
| NodeIOMessage
| ChainStatsUpdate;
/**
* Data type to be sent to the feed. Passing through strings means we can only serialize once,
* no matter how many feed clients are listening in.
*/
export interface SquashedMessages extends Array<Action | Payload> {}
export type SquashedMessages = Array<Action | Payload>;
export type Data = Stringified<SquashedMessages>;
/**
+1 -3
View File
@@ -45,15 +45,13 @@ export type Maybe<T> = T | null | undefined;
* Asynchronous sleep
*/
export function sleep(time: Milliseconds): Promise<void> {
return new Promise<void>((resolve, _reject) => {
return new Promise<void>((resolve) => {
setTimeout(() => resolve(), time);
});
}
export const timestamp = Date.now as () => Timestamp;
export function noop() {}
/**
* Keep track of last N numbers pushed onto internal stack.
* Provides means to get an average of said numbers.
+1
View File
@@ -24,6 +24,7 @@ export type Id<T> = Opaque<number, T>;
/**
* Higher order function producing new auto-incremented `Id`s.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function idGenerator<I extends Id<any>>(): () => I {
let current = 0;
+4 -4
View File
@@ -18,7 +18,7 @@ export abstract class Stringified<T> {
public __PHANTOM__: T;
}
export const parse = (JSON.parse as any) as <T>(val: Stringified<T>) => T;
export const stringify = (JSON.stringify as any) as <T>(
val: T
) => Stringified<T>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const parse = JSON.parse as any as <T>(val: Stringified<T>) => T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const stringify = JSON.stringify as any as <T>(val: T) => Stringified<T>;
+10 -18
View File
@@ -18,15 +18,13 @@ import * as React from 'react';
import './Tile.css';
import { timestamp, Types } from '../common';
export namespace Ago {
export interface Props {
when: Types.Timestamp;
justTime?: boolean;
}
interface AgoProps {
when: Types.Timestamp;
justTime?: boolean;
}
export interface State {
now: Types.Timestamp;
}
interface AgoState {
now: Types.Timestamp;
}
const tickers = new Map<Ago, (ts: Types.Timestamp) => void>();
@@ -43,20 +41,14 @@ function tick() {
tick();
export namespace Ago {
export interface State {
now: Types.Timestamp;
}
}
export class Ago extends React.Component<Ago.Props, Ago.State> {
export class Ago extends React.Component<AgoProps, AgoState> {
public static timeDiff = 0 as Types.Milliseconds;
public state: Ago.State;
public state: AgoState;
private agoStr: string;
constructor(props: Ago.Props) {
constructor(props: AgoProps) {
super(props);
this.state = {
@@ -65,7 +57,7 @@ export class Ago extends React.Component<Ago.Props, Ago.State> {
this.agoStr = this.stringify(props.when, this.state.now);
}
public shouldComponentUpdate(nextProps: Ago.Props, nextState: Ago.State) {
public shouldComponentUpdate(nextProps: AgoProps, nextState: AgoState) {
const nextAgoStr = this.stringify(nextProps.when, nextState.now);
if (this.agoStr !== nextAgoStr) {
+3 -3
View File
@@ -45,11 +45,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
display: flex;
align-items: center;
padding: 0.5em;
border-bottom: 1px solid rgba(0,0,0,0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.AllChains-controls input {
border: 1px solid rgba(0,0,0,0.5);
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 4px;
padding: 0.5em;
flex-grow: 1;
@@ -81,7 +81,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
.AllChains-chain {
padding: 10px 10px;
background: rgb(220,220,220);
background: rgb(220, 220, 220);
color: black;
display: inline-flex;
margin-right: 0.5em;
+5 -7
View File
@@ -21,15 +21,13 @@ import { ChainData } from '../state';
import './AllChains.css';
export namespace AllChains {
export interface Props {
chains: ChainData[];
subscribed: Maybe<Types.GenesisHash>;
connection: Promise<Connection>;
}
interface AllChainsProps {
chains: ChainData[];
subscribed: Maybe<Types.GenesisHash>;
connection: Promise<Connection>;
}
export function AllChains(props: AllChains.Props) {
export function AllChains(props: AllChainsProps) {
const { chains, subscribed, connection } = props;
const [filterText, setFilterText] = React.useState('');
const [sortBy, setSortBy] = React.useState(SortBy.NumberOfNodes);
+22 -20
View File
@@ -17,7 +17,11 @@
import * as React from 'react';
import { Connection } from '../../Connection';
import { Types, Maybe } from '../../common';
import { State as AppState, Update as AppUpdate } from '../../state';
import {
State as AppState,
Update as AppUpdate,
StateSettings,
} from '../../state';
import { getHashData } from '../../utils';
import { Header } from './';
import { List, Map, Settings, Stats } from '../';
@@ -25,28 +29,26 @@ import { Persistent, PersistentObject, PersistentSet } from '../../persist';
import './Chain.css';
export namespace Chain {
export type Display = 'list' | 'map' | 'settings' | 'consensus' | 'stats';
export type ChainDisplay = 'list' | 'map' | 'settings' | 'consensus' | 'stats';
export interface Props {
appState: Readonly<AppState>;
appUpdate: AppUpdate;
connection: Promise<Connection>;
settings: PersistentObject<AppState.Settings>;
pins: PersistentSet<Types.NodeName>;
sortBy: Persistent<Maybe<number>>;
}
export interface State {
display: Display;
}
interface ChainProps {
appState: Readonly<AppState>;
appUpdate: AppUpdate;
connection: Promise<Connection>;
settings: PersistentObject<StateSettings>;
pins: PersistentSet<Types.NodeName>;
sortBy: Persistent<Maybe<number>>;
}
export class Chain extends React.Component<Chain.Props, Chain.State> {
constructor(props: Chain.Props) {
interface ChainState {
display: ChainDisplay;
}
export class Chain extends React.Component<ChainProps, ChainState> {
constructor(props: ChainProps) {
super(props);
let display: Chain.Display = 'list';
let display: ChainDisplay = 'list';
switch (getHashData().tab) {
case 'map':
@@ -91,7 +93,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
return <Settings settings={this.props.settings} />;
}
const { appState, appUpdate, connection, pins, sortBy } = this.props;
const { appState, appUpdate, pins, sortBy } = this.props;
if (display === 'list') {
return (
@@ -115,7 +117,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
throw new Error('invalid `display`: ${display}');
}
private setDisplay = (display: Chain.Display) => {
private setDisplay = (display: ChainDisplay) => {
this.setState({ display });
};
}
+10 -13
View File
@@ -17,7 +17,7 @@
import * as React from 'react';
import { Types, Maybe } from '../../common';
import { formatNumber, secondsWithPrecision } from '../../utils';
import { Tab, Chain } from './';
import { Tab, ChainDisplay } from './';
import { Tile, Ago } from '../';
import blockIcon from '../../icons/cube.svg';
@@ -27,24 +27,21 @@ import lastTimeIcon from '../../icons/watch.svg';
import listIcon from '../../icons/list-alt-regular.svg';
import worldIcon from '../../icons/location.svg';
import settingsIcon from '../../icons/settings.svg';
import consensusIcon from '../../icons/cube-alt.svg';
import statsIcon from '../../icons/graph.svg';
import './Header.css';
export namespace Header {
export interface Props {
best: Types.BlockNumber;
finalized: Types.BlockNumber;
blockTimestamp: Types.Timestamp;
blockAverage: Maybe<Types.Milliseconds>;
currentTab: Chain.Display;
setDisplay: (display: Chain.Display) => void;
}
interface HeaderProps {
best: Types.BlockNumber;
finalized: Types.BlockNumber;
blockTimestamp: Types.Timestamp;
blockAverage: Maybe<Types.Milliseconds>;
currentTab: ChainDisplay;
setDisplay: (display: ChainDisplay) => void;
}
export class Header extends React.Component<Header.Props, {}> {
public shouldComponentUpdate(nextProps: Header.Props) {
export class Header extends React.Component<HeaderProps> {
public shouldComponentUpdate(nextProps: HeaderProps) {
return (
this.props.best !== nextProps.best ||
this.props.finalized !== nextProps.finalized ||
+1 -1
View File
@@ -28,7 +28,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
color: #555;
cursor: pointer;
border-radius: 40px;
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.2);
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2);
}
.Chain-Tab:hover {
+9 -11
View File
@@ -15,24 +15,22 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Chain } from './';
import { ChainDisplay } from './';
import { Icon } from '../';
import { setHashData } from '../../utils';
import './Tab.css';
export namespace Tab {
export interface Props {
label: string;
icon: string;
display: Chain.Display;
current: string;
tab: string;
setDisplay: (display: Chain.Display) => void;
}
interface TabProps {
label: string;
icon: string;
display: ChainDisplay;
current: string;
tab: string;
setDisplay: (display: ChainDisplay) => void;
}
export class Tab extends React.Component<Tab.Props, {}> {
export class Tab extends React.Component<TabProps> {
public render() {
const { label, icon, display, current } = this.props;
const highlight = display === current;
+7 -2
View File
@@ -26,11 +26,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
}
.Chains-extra-subscribed-chain {
position: absolute;
position: absolute;
right: 80px;
top: 0px;
background: #e6007a;
background: linear-gradient(90deg, rgba(0,0,0,0) 0px, #e6007a 30px, #e6007a 100%);
background: linear-gradient(
90deg,
rgba(0, 0, 0, 0) 0px,
#e6007a 30px,
#e6007a 100%
);
z-index: 1;
padding-left: 34px;
}
+9 -10
View File
@@ -25,13 +25,11 @@ import githubIcon from '../icons/mark-github.svg';
import listIcon from '../icons/kebab-horizontal.svg';
import './Chains.css';
export namespace Chains {
export interface Props {
chains: ChainData[];
subscribedHash: Maybe<Types.GenesisHash>;
subscribedData: Maybe<ChainData>;
connection: Promise<Connection>;
}
interface ChainsProps {
chains: ChainData[];
subscribedHash: Maybe<Types.GenesisHash>;
subscribedData: Maybe<ChainData>;
connection: Promise<Connection>;
}
// Average tab width in pixels
@@ -39,12 +37,12 @@ const AVERAGE_TAB_WIDTH = 160;
// Milliseconds, sets the minimum time between the renders
const RENDER_THROTTLE = 1000;
export class Chains extends React.Component<Chains.Props, {}> {
export class Chains extends React.Component<ChainsProps> {
private lastRender = performance.now();
private clicked: Maybe<Types.GenesisHash>;
private subscribedChainInView = false;
public shouldComponentUpdate(nextProps: Chains.Props) {
public shouldComponentUpdate(nextProps: ChainsProps) {
if (nextProps.subscribedHash !== this.clicked) {
this.clicked = nextProps.subscribedHash;
}
@@ -68,7 +66,7 @@ export class Chains extends React.Component<Chains.Props, {}> {
const allChainsHref = subscribedHash
? `#all-chains/${subscribedHash}`
: `#all-chains`;
: '#all-chains';
const subscribedChain =
subscribedData && !this.subscribedChainInView ? (
@@ -93,6 +91,7 @@ export class Chains extends React.Component<Chains.Props, {}> {
href="https://github.com/paritytech/substrate-telemetry"
target="_blank"
title="Fork Me!"
rel="noreferrer"
>
<Icon src={githubIcon} />
</a>
+9 -10
View File
@@ -23,19 +23,17 @@ import searchIcon from '../icons/search.svg';
import './Filter.css';
export namespace Filter {
export interface Props {
onChange: (value: Maybe<(node: Node) => boolean>) => void;
}
interface FilterProps {
onChange: (value: Maybe<(node: Node) => boolean>) => void;
}
export interface State {
value: string;
}
interface FilterState {
value: string;
}
const ESCAPE_KEY = 27;
export class Filter extends React.Component<Filter.Props, {}> {
export class Filter extends React.Component<FilterProps, FilterState> {
public state = {
value: '',
};
@@ -51,8 +49,8 @@ export class Filter extends React.Component<Filter.Props, {}> {
}
public shouldComponentUpdate(
nextProps: Filter.Props,
nextState: Filter.State
nextProps: FilterProps,
nextState: FilterState
): boolean {
return (
this.props.onChange !== nextProps.onChange ||
@@ -110,6 +108,7 @@ export class Filter extends React.Component<Filter.Props, {}> {
return;
}
// Ignore events dispatched to other elements that want to use it
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (['INPUT', 'TEXTAREA'].includes((event.target as any)?.tagName)) {
return;
}
+2 -1
View File
@@ -26,7 +26,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
display: inline-block;
}
.Icon svg, .Icon-symbol-root symbol {
.Icon svg,
.Icon-symbol-root symbol {
width: 1em;
height: 1em;
}
+7 -9
View File
@@ -18,12 +18,10 @@ import * as React from 'react';
import './Icon.css';
import { getSVGShadowRoot, W3SVG } from '../utils';
export namespace Icon {
export interface Props {
src: string;
className?: string;
onClick?: () => void;
}
interface IconProps {
src: string;
className?: string;
onClick?: () => void;
}
const symbols = new Map<string, string>();
@@ -67,10 +65,10 @@ function renderShadowIcon(src: string): string {
return symbol;
}
export class Icon extends React.Component<Icon.Props, {}> {
public props: Icon.Props;
export class Icon extends React.Component<IconProps> {
public props: IconProps;
public shouldComponentUpdate(nextProps: Icon.Props) {
public shouldComponentUpdate(nextProps: IconProps) {
return (
this.props.src !== nextProps.src ||
this.props.className !== nextProps.className
@@ -16,12 +16,12 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Truncate, Tooltip } from '../../';
import { Truncate, Tooltip, TooltipCopyCallback } from '../../';
import icon from '../../../icons/file-binary.svg';
export class BlockHashColumn extends React.Component<Column.Props, {}> {
export class BlockHashColumn extends React.Component<ColumnProps> {
public static readonly label = 'Block Hash';
public static readonly icon = icon;
public static readonly width = 154;
@@ -29,9 +29,9 @@ export class BlockHashColumn extends React.Component<Column.Props, {}> {
public static readonly sortBy = ({ hash }: Node) => hash || '';
private data: Maybe<string>;
private copy: Maybe<Tooltip.CopyCallback>;
private copy: Maybe<TooltipCopyCallback>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.hash;
}
@@ -48,7 +48,7 @@ export class BlockHashColumn extends React.Component<Column.Props, {}> {
);
}
private onCopy = (copy: Tooltip.CopyCallback) => {
private onCopy = (copy: TooltipCopyCallback) => {
this.copy = copy;
};
@@ -15,12 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { formatNumber } from '../../../utils';
import icon from '../../../icons/cube.svg';
export class BlockNumberColumn extends React.Component<Column.Props, {}> {
export class BlockNumberColumn extends React.Component<ColumnProps> {
public static readonly label = 'Block';
public static readonly icon = icon;
public static readonly width = 88;
@@ -29,7 +29,7 @@ export class BlockNumberColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.height;
}
@@ -16,12 +16,12 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { milliOrSecond } from '../../../utils';
import icon from '../../../icons/dashboard.svg';
export class BlockPropagationColumn extends React.Component<Column.Props, {}> {
export class BlockPropagationColumn extends React.Component<ColumnProps> {
public static readonly label = 'Block Propagation Time';
public static readonly icon = icon;
public static readonly width = 58;
@@ -31,7 +31,7 @@ export class BlockPropagationColumn extends React.Component<Column.Props, {}> {
private data: Maybe<number>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.propagationTime;
}
@@ -15,12 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { secondsWithPrecision } from '../../../utils';
import icon from '../../../icons/history.svg';
export class BlockTimeColumn extends React.Component<Column.Props, {}> {
export class BlockTimeColumn extends React.Component<ColumnProps> {
public static readonly label = 'Block Time';
public static readonly icon = icon;
public static readonly width = 80;
@@ -30,7 +30,7 @@ export class BlockTimeColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.blockTime;
}
@@ -58,4 +58,3 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
.Column--a:hover {
text-decoration: underline;
}
+30 -33
View File
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Types, Maybe, timestamp } from '../../../common';
import { Node } from '../../../state';
@@ -61,41 +60,39 @@ export type Column =
| typeof LastBlockColumn
| typeof UptimeColumn;
export namespace Column {
export interface Props {
node: Node;
export interface ColumnProps {
node: Node;
}
export function formatBytes(
bytes: number,
stamp: Maybe<Types.Timestamp>
): string {
const ago = stamp ? ` (${formatStamp(stamp)})` : '';
if (bytes >= 1024 * 1024 * 1024) {
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB${ago}`;
} else if (bytes >= 1024 * 1024) {
return `${(bytes / (1024 * 1024)).toFixed(1)} MB${ago}`;
} else if (bytes >= 1000) {
return `${(bytes / 1024).toFixed(1)} kB${ago}`;
} else {
return `${bytes} B${ago}`;
}
}
export function formatBytes(
bytes: number,
stamp: Maybe<Types.Timestamp>
): string {
const ago = stamp ? ` (${formatStamp(stamp)})` : '';
export function formatBandwidth(
bps: number,
stamp: Maybe<Types.Timestamp>
): string {
const ago = stamp ? ` (${formatStamp(stamp)})` : '';
if (bytes >= 1024 * 1024 * 1024) {
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB${ago}`;
} else if (bytes >= 1024 * 1024) {
return `${(bytes / (1024 * 1024)).toFixed(1)} MB${ago}`;
} else if (bytes >= 1000) {
return `${(bytes / 1024).toFixed(1)} kB${ago}`;
} else {
return `${bytes} B${ago}`;
}
}
export function formatBandwidth(
bps: number,
stamp: Maybe<Types.Timestamp>
): string {
const ago = stamp ? ` (${formatStamp(stamp)})` : '';
if (bps >= 1024 * 1024) {
return `${(bps / (1024 * 1024)).toFixed(1)} MB/s${ago}`;
} else if (bps >= 1000) {
return `${(bps / 1024).toFixed(1)} kB/s${ago}`;
} else {
return `${bps | 0} B/s${ago}`;
}
if (bps >= 1024 * 1024) {
return `${(bps / (1024 * 1024)).toFixed(1)} MB/s${ago}`;
} else if (bps >= 1000) {
return `${(bps / 1024).toFixed(1)} kB/s${ago}`;
} else {
return `${bps | 0} B/s${ago}`;
}
}
@@ -15,13 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Types, Maybe, timestamp } from '../../../common';
import { Column, BANDWIDTH_SCALE } from './';
import { ColumnProps, formatBandwidth, BANDWIDTH_SCALE } from './';
import { Node } from '../../../state';
import { Sparkline } from '../../';
import icon from '../../../icons/cloud-download.svg';
export class DownloadColumn extends React.Component<Column.Props, {}> {
export class DownloadColumn extends React.Component<ColumnProps> {
public static readonly label = 'Download Bandwidth';
public static readonly icon = icon;
public static readonly width = 40;
@@ -31,7 +30,7 @@ export class DownloadColumn extends React.Component<Column.Props, {}> {
private data: Array<number> = [];
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
// Diffing by ref, as data is an immutable array
return this.data !== nextProps.node.download;
}
@@ -51,7 +50,7 @@ export class DownloadColumn extends React.Component<Column.Props, {}> {
width={44}
height={16}
stroke={1}
format={Column.formatBandwidth}
format={formatBandwidth}
values={download}
stamps={chartstamps}
minScale={BANDWIDTH_SCALE}
@@ -15,12 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { formatNumber } from '../../../utils';
import icon from '../../../icons/cube-alt.svg';
export class FinalizedBlockColumn extends React.Component<Column.Props, {}> {
export class FinalizedBlockColumn extends React.Component<ColumnProps> {
public static readonly label = 'Finalized Block';
public static readonly icon = icon;
public static readonly width = 88;
@@ -29,7 +29,7 @@ export class FinalizedBlockColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.finalized;
}
@@ -16,12 +16,12 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Truncate, Tooltip } from '../../';
import { Truncate, Tooltip, TooltipCopyCallback } from '../../';
import icon from '../../../icons/file-binary.svg';
export class FinalizedHashColumn extends React.Component<Column.Props, {}> {
export class FinalizedHashColumn extends React.Component<ColumnProps> {
public static readonly label = 'Finalized Block Hash';
public static readonly icon = icon;
public static readonly width = 154;
@@ -30,9 +30,9 @@ export class FinalizedHashColumn extends React.Component<Column.Props, {}> {
finalizedHash || '';
private data: Maybe<string>;
private copy: Maybe<Tooltip.CopyCallback>;
private copy: Maybe<TooltipCopyCallback>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.finalizedHash;
}
@@ -49,7 +49,7 @@ export class FinalizedHashColumn extends React.Component<Column.Props, {}> {
);
}
private onCopy = (copy: Tooltip.CopyCallback) => {
private onCopy = (copy: TooltipCopyCallback) => {
this.copy = copy;
};
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Tooltip, Icon } from '../../';
import icon from '../../../icons/terminal.svg';
@@ -69,7 +69,7 @@ const ICONS = {
};
const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
export class ImplementationColumn extends React.Component<Column.Props, {}> {
export class ImplementationColumn extends React.Component<ColumnProps> {
public static readonly label = 'Implementation';
public static readonly icon = icon;
public static readonly width = 90;
@@ -80,7 +80,7 @@ export class ImplementationColumn extends React.Component<Column.Props, {}> {
private implementation: string;
private version: string;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
if (this.props.node === nextProps.node) {
// Implementation can't change unless we got a new node
return false;
@@ -15,12 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Ago } from '../../';
import icon from '../../../icons/watch.svg';
export class LastBlockColumn extends React.Component<Column.Props, {}> {
export class LastBlockColumn extends React.Component<ColumnProps> {
public static readonly label = 'Last Block Time';
public static readonly icon = icon;
public static readonly width = 100;
@@ -30,7 +30,7 @@ export class LastBlockColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.blockTimestamp;
}
@@ -16,12 +16,12 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Truncate, Tooltip } from '../../';
import icon from '../../../icons/location.svg';
export class LocationColumn extends React.Component<Column.Props, {}> {
export class LocationColumn extends React.Component<ColumnProps> {
public static readonly label = 'Location';
public static readonly icon = icon;
public static readonly width = 140;
@@ -30,7 +30,7 @@ export class LocationColumn extends React.Component<Column.Props, {}> {
private data: Maybe<string>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.city;
}
@@ -15,19 +15,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Truncate, Tooltip } from '../../';
import icon from '../../../icons/server.svg';
export class NameColumn extends React.Component<Column.Props, {}> {
export class NameColumn extends React.Component<ColumnProps> {
public static readonly label = 'Node';
public static readonly icon = icon;
public static readonly setting = null;
public static readonly width = null;
public static readonly sortBy = ({ sortableName }: Node) => sortableName;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
// Node name only changes when the node does
return this.props.node !== nextProps.node;
}
@@ -16,13 +16,13 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Truncate } from '../../';
import { Tooltip } from '../../';
import icon from '../../../icons/fingerprint.svg';
export class NetworkIdColumn extends React.Component<Column.Props, {}> {
export class NetworkIdColumn extends React.Component<ColumnProps> {
public static readonly label = 'Network ID';
public static readonly icon = icon;
public static readonly width = 90;
@@ -31,7 +31,7 @@ export class NetworkIdColumn extends React.Component<Column.Props, {}> {
private data: Maybe<string>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.networkId;
}
@@ -15,11 +15,11 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/broadcast.svg';
export class PeersColumn extends React.Component<Column.Props, {}> {
export class PeersColumn extends React.Component<ColumnProps> {
public static readonly label = 'Peer Count';
public static readonly icon = icon;
public static readonly width = 26;
@@ -28,7 +28,7 @@ export class PeersColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.peers;
}
@@ -15,13 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Types, Maybe, timestamp } from '../../../common';
import { Column, BANDWIDTH_SCALE } from './';
import { ColumnProps, formatBytes, BANDWIDTH_SCALE } from './';
import { Node } from '../../../state';
import { Sparkline } from '../../';
import icon from '../../../icons/git-branch.svg';
export class StateCacheColumn extends React.Component<Column.Props, {}> {
export class StateCacheColumn extends React.Component<ColumnProps> {
public static readonly label = 'State Cache Size';
public static readonly icon = icon;
public static readonly width = 40;
@@ -31,7 +30,7 @@ export class StateCacheColumn extends React.Component<Column.Props, {}> {
private data: Array<number> = [];
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
// Diffing by ref, as data is an immutable array
return this.data !== nextProps.node.stateCacheSize;
}
@@ -51,7 +50,7 @@ export class StateCacheColumn extends React.Component<Column.Props, {}> {
width={44}
height={16}
stroke={1}
format={Column.formatBytes}
format={formatBytes}
values={stateCacheSize}
stamps={chartstamps}
minScale={BANDWIDTH_SCALE}
@@ -15,11 +15,11 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/inbox.svg';
export class TxsColumn extends React.Component<Column.Props, {}> {
export class TxsColumn extends React.Component<ColumnProps> {
public static readonly label = 'Transactions in Queue';
public static readonly icon = icon;
public static readonly width = 26;
@@ -28,7 +28,7 @@ export class TxsColumn extends React.Component<Column.Props, {}> {
private data = 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.txs;
}
@@ -15,13 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Types, Maybe, timestamp } from '../../../common';
import { Column, BANDWIDTH_SCALE } from './';
import { ColumnProps, formatBandwidth, BANDWIDTH_SCALE } from './';
import { Node } from '../../../state';
import { Sparkline } from '../../';
import icon from '../../../icons/cloud-upload.svg';
export class UploadColumn extends React.Component<Column.Props, {}> {
export class UploadColumn extends React.Component<ColumnProps> {
public static readonly label = 'Upload Bandwidth';
public static readonly icon = icon;
public static readonly width = 40;
@@ -31,7 +30,7 @@ export class UploadColumn extends React.Component<Column.Props, {}> {
private data: Array<number> = [];
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
// Diffing by ref, as data is an immutable array
return this.data !== nextProps.node.upload;
}
@@ -51,7 +50,7 @@ export class UploadColumn extends React.Component<Column.Props, {}> {
width={44}
height={16}
stroke={1}
format={Column.formatBandwidth}
format={formatBandwidth}
values={upload}
stamps={chartstamps}
minScale={BANDWIDTH_SCALE}
@@ -15,19 +15,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Ago } from '../../';
import icon from '../../../icons/pulse.svg';
export class UptimeColumn extends React.Component<Column.Props, {}> {
export class UptimeColumn extends React.Component<ColumnProps> {
public static readonly label = 'Node Uptime';
public static readonly icon = icon;
public static readonly width = 58;
public static readonly setting = 'uptime';
public static readonly sortBy = ({ startupTime }: Node) => startupTime || 0;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
// Uptime only changes when the node does
return this.props.node !== nextProps.node;
}
@@ -16,12 +16,12 @@
import * as React from 'react';
import { Maybe } from '../../../common';
import { Column } from './';
import { ColumnProps } from './';
import { Node } from '../../../state';
import { Tooltip, PolkadotIcon } from '../../';
import { Tooltip, PolkadotIcon, TooltipCopyCallback } from '../../';
import icon from '../../../icons/shield.svg';
export class ValidatorColumn extends React.Component<Column.Props, {}> {
export class ValidatorColumn extends React.Component<ColumnProps> {
public static readonly label = 'Validator';
public static readonly icon = icon;
public static readonly width = 16;
@@ -29,9 +29,9 @@ export class ValidatorColumn extends React.Component<Column.Props, {}> {
public static readonly sortBy = ({ validator }: Node) => validator || '';
private data: Maybe<string>;
private copy: Maybe<Tooltip.CopyCallback>;
private copy: Maybe<TooltipCopyCallback>;
public shouldComponentUpdate(nextProps: Column.Props) {
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.validator;
}
@@ -56,7 +56,7 @@ export class ValidatorColumn extends React.Component<Column.Props, {}> {
);
}
private onCopy = (copy: Tooltip.CopyCallback) => {
private onCopy = (copy: TooltipCopyCallback) => {
this.copy = copy;
};
+6 -13
View File
@@ -29,25 +29,18 @@ const ROW_MARGIN = 5;
import './List.css';
export namespace List {
export interface Props {
appState: Readonly<AppState>;
appUpdate: AppUpdate;
pins: PersistentSet<Types.NodeName>;
sortBy: Persistent<Maybe<number>>;
}
export interface State {
filter: Maybe<(node: Node) => boolean>;
viewportHeight: number;
}
interface ListProps {
appState: Readonly<AppState>;
appUpdate: AppUpdate;
pins: PersistentSet<Types.NodeName>;
sortBy: Persistent<Maybe<number>>;
}
// Helper for readability, used as `key` prop for each `Row`
// of the `List`, so that we can maximize re-using DOM elements.
type Key = number;
export class List extends React.Component<List.Props, {}> {
export class List extends React.Component<ListProps> {
public state = {
filter: null,
viewportHeight: viewport().height,
+11 -18
View File
@@ -15,9 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Types, Maybe } from '../../common';
import { Types } from '../../common';
import { Node } from '../../state';
import { Persistent, PersistentSet } from '../../persist';
import { PersistentSet } from '../../persist';
import {
Column,
NameColumn,
@@ -42,24 +42,17 @@ import {
import './Row.css';
export namespace Row {
export interface Props {
node: Node;
pins: PersistentSet<Types.NodeName>;
columns: Column[];
}
export interface State {
update: number;
}
}
interface HeaderProps {
interface RowProps {
node: Node;
pins: PersistentSet<Types.NodeName>;
columns: Column[];
sortBy: Persistent<Maybe<number>>;
}
export class Row extends React.Component<Row.Props, Row.State> {
interface RowState {
update: number;
}
export class Row extends React.Component<RowProps, RowState> {
public static readonly columns: Column[] = [
NameColumn,
ValidatorColumn,
@@ -83,7 +76,7 @@ export class Row extends React.Component<Row.Props, Row.State> {
private renderedChangeRef = 0;
public shouldComponentUpdate(nextProps: Row.Props): boolean {
public shouldComponentUpdate(nextProps: RowProps): boolean {
return (
this.props.node.id !== nextProps.node.id ||
this.renderedChangeRef !== nextProps.node.changeRef
+1 -1
View File
@@ -39,4 +39,4 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
.THeadCell-container {
position: relative;
display: inline-block;
}
}
+6 -8
View File
@@ -21,23 +21,21 @@ import { Persistent } from '../../persist';
import './THead.css';
export namespace THead {
export interface Props {
columns: Column[];
sortBy: Persistent<Maybe<number>>;
}
interface THeadProps {
columns: Column[];
sortBy: Persistent<Maybe<number>>;
}
export class THead extends React.Component<THead.Props, {}> {
export class THead extends React.Component<THeadProps> {
private sortBy: Maybe<number>;
constructor(props: THead.Props) {
constructor(props: THeadProps) {
super(props);
this.sortBy = props.sortBy.get();
}
public shouldComponentUpdate(nextProps: THead.Props) {
public shouldComponentUpdate(nextProps: THeadProps) {
return this.sortBy !== nextProps.sortBy.get();
}
+6 -8
View File
@@ -23,16 +23,14 @@ import { Persistent } from '../../persist';
import sortAscIcon from '../../icons/triangle-up.svg';
import sortDescIcon from '../../icons/triangle-down.svg';
export namespace THeadCell {
export interface Props {
column: Column;
index: number;
last: number;
sortBy: Persistent<Maybe<number>>;
}
interface THeadCellProps {
column: Column;
index: number;
last: number;
sortBy: Persistent<Maybe<number>>;
}
export class THeadCell extends React.Component<THeadCell.Props, {}> {
export class THeadCell extends React.Component<THeadCellProps> {
public render() {
const { column, index, last } = this.props;
const { icon, width, label } = column;
+16 -18
View File
@@ -36,27 +36,25 @@ import lastTimeIcon from '../../icons/watch.svg';
import './Location.css';
export namespace Location {
export type Quarter = 0 | 1 | 2 | 3;
export type LocationQuarter = 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;
}
interface LocationProps {
node: Node;
position: LocationPosition;
focused: boolean;
}
export class Location extends React.Component<Location.Props, Location.State> {
export interface LocationPosition {
left: number;
top: number;
quarter: LocationQuarter;
}
interface LocationState {
hover: boolean;
}
export class Location extends React.Component<LocationProps, LocationState> {
public readonly state = { hover: false };
public render() {
+17 -19
View File
@@ -18,7 +18,7 @@ import * as React from 'react';
import { Types, Maybe } from '../../common';
import { Filter } from '../';
import { State as AppState, Node } from '../../state';
import { Location } from './';
import { Location, LocationQuarter, LocationPosition } from './';
import { viewport } from '../../utils';
const MAP_RATIO = 800 / 350;
@@ -27,22 +27,20 @@ const HEADER = 148;
import './Map.css';
export namespace Map {
export interface Props {
appState: Readonly<AppState>;
}
export interface State {
filter: Maybe<(node: Node) => boolean>;
width: number;
height: number;
top: number;
left: number;
}
interface MapProps {
appState: Readonly<AppState>;
}
export class Map extends React.Component<Map.Props, Map.State> {
public state: Map.State = {
interface MapState {
filter: Maybe<(node: Node) => boolean>;
width: number;
height: number;
top: number;
left: number;
}
export class Map extends React.Component<MapProps, MapState> {
public state: MapState = {
filter: null,
width: 0,
height: 0,
@@ -98,7 +96,7 @@ export class Map extends React.Component<Map.Props, Map.State> {
private pixelPosition(
lat: Types.Latitude,
lon: Types.Longitude
): Location.Position {
): LocationPosition {
const { state } = this;
// Longitude ranges -180 (west) to +180 (east)
@@ -108,14 +106,14 @@ export class Map extends React.Component<Map.Props, Map.State> {
((90 - lat) / 180) * state.height * MAP_HEIGHT_ADJUST + state.top
);
let quarter: Location.Quarter = 0;
let quarter: LocationQuarter = 0;
if (lon > 0) {
quarter = (quarter | 1) as Location.Quarter;
quarter = (quarter | 1) as LocationQuarter;
}
if (lat < 0) {
quarter = (quarter | 2) as Location.Quarter;
quarter = (quarter | 2) as LocationQuarter;
}
return { left, top, quarter };
+4 -5
View File
@@ -21,14 +21,13 @@ import { State } from '../state';
import offlineIcon from '../icons/zap.svg';
import upgradeIcon from '../icons/flame.svg';
export namespace OfflineIndicator {
export interface Props {
status: State['status'];
}
interface OfflineIndicatorProps {
status: State['status'];
}
export function OfflineIndicator(
props: OfflineIndicator.Props
props: OfflineIndicatorProps
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): React.ReactElement<any> | null {
switch (props.status) {
case 'online':
+15 -21
View File
@@ -74,9 +74,7 @@ const OUTER_CIRCLE: Circle = {
fill: '#eee',
};
function getRotation(
isSixPoint: boolean
): {
function getRotation(isSixPoint: boolean): {
r: number;
ro2: number;
r3o4: number;
@@ -184,25 +182,21 @@ function generate(address: string, isSixPoint = false): Circle[] {
}
return [OUTER_CIRCLE].concat(
getCircleXY(isSixPoint).map(
([cx, cy], index): Circle => {
return {
cx,
cy,
r: Z,
fill: colors[index] || 'rgb(255,255,255)',
};
}
)
getCircleXY(isSixPoint).map(([cx, cy], index): Circle => {
return {
cx,
cy,
r: Z,
fill: colors[index] || 'rgb(255,255,255)',
};
})
);
}
export namespace PolkadotIcon {
export interface Props {
account: string;
size: number;
className?: string;
}
interface PolkadotIconProps {
account: string;
size: number;
className?: string;
}
const rendered = new Set<string>();
@@ -233,8 +227,8 @@ function renderShadowIcon(account: string) {
}
}
export class PolkadotIcon extends React.Component<PolkadotIcon.Props, {}> {
public shouldComponentUpdate(nextProps: PolkadotIcon.Props) {
export class PolkadotIcon extends React.Component<PolkadotIconProps> {
public shouldComponentUpdate(nextProps: PolkadotIconProps) {
return (
this.props.account !== nextProps.account ||
this.props.size !== nextProps.size
+7 -9
View File
@@ -16,21 +16,19 @@
import * as React from 'react';
import { Icon } from '../';
import { State } from '../../state';
import { StateSettings } from '../../state';
import { PersistentObject } from '../../persist';
import './Setting.css';
export namespace Setting {
export interface Props {
icon: string;
label: string;
setting: keyof State.Settings;
settings: PersistentObject<State.Settings>;
}
interface SettingProps {
icon: string;
label: string;
setting: keyof StateSettings;
settings: PersistentObject<StateSettings>;
}
export class Setting extends React.Component<Setting.Props, {}> {
export class Setting extends React.Component<SettingProps> {
public render() {
const { icon, label, setting, settings } = this.props;
+4 -14
View File
@@ -15,28 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { Maybe } from '../../common';
import { State as AppState } from '../../state';
import { StateSettings } from '../../state';
import { Setting } from './';
import { Row } from '../List';
import { PersistentObject } from '../../persist';
import './Settings.css';
export namespace Settings {
export type Display = 'list' | 'map' | 'settings';
export interface Props {
settings: PersistentObject<AppState.Settings>;
}
export interface State {
display: Display;
filter: Maybe<string>;
}
interface SettingsProps {
settings: PersistentObject<StateSettings>;
}
export class Settings extends React.Component<Settings.Props, {}> {
export class Settings extends React.Component<SettingsProps> {
public render() {
const { settings } = this.props;
+14 -16
View File
@@ -16,28 +16,26 @@
import * as React from 'react';
import { Types, Maybe } from '../common';
import { Tooltip } from './';
import { Tooltip, TooltipUpdateCallback } from './';
import './Sparkline.css';
export namespace Sparkline {
export interface Props {
stroke: number;
width: number;
height: number;
values: number[];
stamps?: Types.Timestamp[];
minScale?: number;
format?: (value: number, stamp: Maybe<Types.Timestamp>) => string;
}
interface SparklineProps {
stroke: number;
width: number;
height: number;
values: number[];
stamps?: Types.Timestamp[];
minScale?: number;
format?: (value: number, stamp: Maybe<Types.Timestamp>) => string;
}
export class Sparkline extends React.Component<Sparkline.Props, {}> {
export class Sparkline extends React.Component<SparklineProps> {
private cursor: SVGPathElement;
private update: Tooltip.UpdateCallback;
private update: TooltipUpdateCallback;
public shouldComponentUpdate(nextProps: Sparkline.Props): boolean {
const { stroke, width, height, minScale, format, values } = this.props;
public shouldComponentUpdate(nextProps: SparklineProps): boolean {
const { stroke, width, height, format, values } = this.props;
return (
values !== nextProps.values ||
@@ -93,7 +91,7 @@ export class Sparkline extends React.Component<Sparkline.Props, {}> {
this.cursor = cursor;
};
private onTooltipInit = (update: Tooltip.UpdateCallback) => {
private onTooltipInit = (update: TooltipUpdateCallback) => {
this.update = update;
};
+3 -9
View File
@@ -17,18 +17,12 @@
import * as React from 'react';
import { Maybe } from '../../common';
import { State as AppState } from '../../state';
import { Row } from '../List';
import { PersistentObject } from '../../persist';
import { Ranking, Range } from '../../common/types';
import './Stats.css';
export namespace Stats {
export type Display = 'list' | 'map' | 'Stats';
export interface Props {
appState: Readonly<AppState>;
}
interface StatsProps {
appState: Readonly<AppState>;
}
function displayPercentage(percent: number): string {
@@ -138,7 +132,7 @@ function formatScore(value: Range): string {
return (min / 100).toFixed(1) + 'x';
}
export class Stats extends React.Component<Stats.Props, {}> {
export class Stats extends React.Component<StatsProps> {
public render() {
const { appState } = this.props;
+5 -7
View File
@@ -18,15 +18,13 @@ import * as React from 'react';
import './Tile.css';
import { Icon } from './Icon';
export namespace Tile {
export interface Props {
title: string;
icon: string;
children?: React.ReactNode;
}
interface TileProps {
title: string;
icon: string;
children?: React.ReactNode;
}
export function Tile(props: Tile.Props) {
export function Tile(props: TileProps) {
return (
<div className="Tile">
<Icon src={props.icon} />
-1
View File
@@ -95,4 +95,3 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
:hover > .Tooltip {
display: block;
}
+9 -15
View File
@@ -19,22 +19,16 @@ import { Maybe } from '../common';
import './Tooltip.css';
export namespace Tooltip {
export interface Props {
text: string;
copy?: (cb: CopyCallback) => void;
position?: 'left' | 'right' | 'center';
onInit?: (update: UpdateCallback) => void;
}
export interface State {
copied: boolean;
}
export type UpdateCallback = (text: string) => void;
export type CopyCallback = Maybe<() => void>;
interface TooltipProps {
text: string;
copy?: (cb: TooltipCopyCallback) => void;
position?: 'left' | 'right' | 'center';
onInit?: (update: TooltipUpdateCallback) => void;
}
export type TooltipUpdateCallback = (text: string) => void;
export type TooltipCopyCallback = Maybe<() => void>;
function copyToClipboard(text: string) {
const el = document.createElement('textarea');
el.value = text;
@@ -49,7 +43,7 @@ export function Tooltip({
position,
copy,
onInit,
}: Tooltip.Props): JSX.Element {
}: TooltipProps): JSX.Element {
const [copied, setCopied] = React.useState<boolean>(false);
const [timer, setTimer] = React.useState<NodeJS.Timer | null>(null);
const el = React.useRef<HTMLDivElement>(null);
+5 -7
View File
@@ -16,15 +16,13 @@
import * as React from 'react';
export namespace Truncate {
export interface Props {
text: string;
chars?: number;
}
interface TruncateProps {
text: string;
chars?: number;
}
export class Truncate extends React.Component<Truncate.Props, {}> {
public shouldComponentUpdate(nextProps: Truncate.Props): boolean {
export class Truncate extends React.Component<TruncateProps> {
public shouldComponentUpdate(nextProps: TruncateProps): boolean {
return this.props.text !== nextProps.text;
}
+1 -1
View File
@@ -1 +1 @@
<svg height='300px' width='300px' 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" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"><g transform="translate(0,-952.36218)"><path style="enable-background:accumulate;" d="m 50.000002,960.75284 -36.25,14.28129 36.25,14.2812 36.249996,-14.2812 -36.249996,-14.28129 z m -39,17.49999 0,51.12497 37,14.5937 0,-51.12487 -37,-14.5938 z m 77.999996,0 -36.999996,14.5938 0,51.12487 36.999996,-14.5937 0,-51.12497 z" stroke="none" marker="none" visibility="visible" display="inline" overflow="visible"></path></g></svg>
<svg height='300px' width='300px' xmlnsDc="http://purl.org/dc/elements/1.1/" xmlnsCc="http://creativecommons.org/ns#" xmlnsRdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlnsSvg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlnsSodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlnsInkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"><g transform="translate(0,-952.36218)"><path style="enable-background:accumulate;" d="m 50.000002,960.75284 -36.25,14.28129 36.25,14.2812 36.249996,-14.2812 -36.249996,-14.28129 z m -39,17.49999 0,51.12497 37,14.5937 0,-51.12487 -37,-14.5938 z m 77.999996,0 -36.999996,14.5938 0,51.12487 36.999996,-14.5937 0,-51.12497 z" stroke="none" marker="none" visibility="visible" display="inline" overflow="visible"></path></g></svg>

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 850 B

+1 -1
View File
@@ -1 +1 @@
<svg height='300px' width='300px' 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" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"><g transform="translate(0,-952.36218)"><path style="text-indent:0;text-transform:none;direction:ltr;block-progression:tb;baseline-shift:baseline;enable-background:accumulate;" d="m 49.625002,958.37952 a 3.0003,3.0003 0 0 0 -0.7188,0.1875 l -37,14.71875 a 3.0003,3.0003 0 0 0 -1.9062,2.8125 l 0,52.56253 a 3.0003,3.0003 0 0 0 1.9062,2.7812 l 37,14.7188 a 3.0003,3.0003 0 0 0 2.1876,0 l 36.999997,-14.7188 a 3.0003,3.0003 0 0 0 1.9062,-2.7812 l 0,-52.56253 a 3.0003,3.0003 0 0 0 -1.9062,-2.8125 L 51.093802,958.56702 a 3.0003,3.0003 0 0 0 -1.4688,-0.1875 z m 0.375,6.21875 28.875,11.5 -28.875,11.46873 -28.875,-11.46873 28.875,-11.5 z m -34,15.90623 31,12.3438 0,46.0937 -31,-12.3437 0,-46.0938 z m 68,0 0,46.0938 -31,12.3437 0,-46.0937 31,-12.3438 z" fill-opacity="1" stroke="none" marker="none" visibility="visible" display="inline" overflow="visible"></path></g></svg>
<svg height='300px' width='300px' xmlnsDc="http://purl.org/dc/elements/1.1/" xmlnsCc="http://creativecommons.org/ns#" xmlnsRdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlnsSvg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlnsSodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlnsInkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"><g transform="translate(0,-952.36218)"><path style="text-indent:0;text-transform:none;direction:ltr;block-progression:tb;baseline-shift:baseline;enable-background:accumulate;" d="m 49.625002,958.37952 a 3.0003,3.0003 0 0 0 -0.7188,0.1875 l -37,14.71875 a 3.0003,3.0003 0 0 0 -1.9062,2.8125 l 0,52.56253 a 3.0003,3.0003 0 0 0 1.9062,2.7812 l 37,14.7188 a 3.0003,3.0003 0 0 0 2.1876,0 l 36.999997,-14.7188 a 3.0003,3.0003 0 0 0 1.9062,-2.7812 l 0,-52.56253 a 3.0003,3.0003 0 0 0 -1.9062,-2.8125 L 51.093802,958.56702 a 3.0003,3.0003 0 0 0 -1.4688,-0.1875 z m 0.375,6.21875 28.875,11.5 -28.875,11.46873 -28.875,-11.46873 28.875,-11.5 z m -34,15.90623 31,12.3438 0,46.0937 -31,-12.3437 0,-46.0938 z m 68,0 0,46.0938 -31,12.3437 0,-46.0937 31,-12.3438 z" fill-opacity="1" stroke="none" marker="none" visibility="visible" display="inline" overflow="visible"></path></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlnsSvg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="12"
height="16"

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 313 B

+1 -1
View File
@@ -2,7 +2,7 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlnsSvg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="100mm"
height="100mm"

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

+4 -2
View File
@@ -15,11 +15,13 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import App from './App';
import './index.css';
import { unregister } from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);
root.render(<App />);
unregister();
+4 -2
View File
@@ -43,7 +43,8 @@ export class Persistent<Data> {
window.addEventListener('storage', (event) => {
if (event.key === this.key) {
this.value = parse((event.newValue as any) as Stringified<Data>);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.value = parse(event.newValue as any as Stringified<Data>);
this.onChange(this.value);
}
@@ -58,7 +59,8 @@ export class Persistent<Data> {
this.value = value;
window.localStorage.setItem(
this.key,
(stringify(this.value) as any) as string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stringify(this.value) as any as string
);
this.onChange(this.value);
}
+19 -26
View File
@@ -237,31 +237,24 @@ export function bindState(bind: React.Component, state: State): Update {
};
}
export namespace State {
export interface Settings {
location: boolean;
validator: boolean;
implementation: boolean;
networkId: boolean;
peers: boolean;
txs: boolean;
upload: boolean;
download: boolean;
stateCacheSize: boolean;
blocknumber: boolean;
blockhash: boolean;
finalized: boolean;
finalizedhash: boolean;
blocktime: boolean;
blockpropagation: boolean;
blocklasttime: boolean;
uptime: boolean;
}
export interface SortBy {
column: string;
reverse: boolean;
}
export interface StateSettings {
location: boolean;
validator: boolean;
implementation: boolean;
networkId: boolean;
peers: boolean;
txs: boolean;
upload: boolean;
download: boolean;
stateCacheSize: boolean;
blocknumber: boolean;
blockhash: boolean;
finalized: boolean;
finalizedhash: boolean;
blocktime: boolean;
blockpropagation: boolean;
blocklasttime: boolean;
uptime: boolean;
}
export interface State {
@@ -275,7 +268,7 @@ export interface State {
subscribed: Maybe<Types.GenesisHash>;
chains: Map<Types.GenesisHash, ChainData>;
nodes: SortedCollection<Node>;
settings: Readonly<State.Settings>;
settings: Readonly<StateSettings>;
pins: Readonly<Set<Types.NodeName>>;
sortBy: Readonly<Maybe<number>>;
selectedColumns: Column[];
+6 -2
View File
@@ -4,7 +4,11 @@
"outDir": "build",
"module": "esnext",
"target": "ES2017",
"lib": ["es6", "dom"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"sourceMap": true,
"allowJs": false,
"jsx": "react",
@@ -25,5 +29,5 @@
"skipLibCheck": true
},
"exclude": ["node_modules", "build"],
"include": ["src/**/*.ts", "src/**/*.tsx", "declarations/**/*.d.ts"]
"include": ["src", "declarations/**/*.d.ts"]
}
-29
View File
@@ -1,29 +0,0 @@
{
"extends": [
"tslint:recommended",
"tslint-react",
"tslint-plugin-prettier",
"tslint-config-prettier"
],
"linterOptions": {
"exclude": ["config/**/*.js", "node_modules/**/*.ts"]
},
"rules": {
"prettier": true,
"ordered-imports": false,
"object-literal-sort-keys": false,
"no-console": false,
"no-empty": false,
"no-namespace": false,
"no-bitwise": false,
"quotemark": [true, "single", "jsx-double"],
"semicolon": [true, "always", "ignore-bound-class-methods"],
"interface-name": false,
"array-type": false,
"max-classes-per-file": false,
"variable-name": [
true,
"allow-leading-underscore"
]
}
}
+7482 -7936
View File
File diff suppressed because it is too large Load Diff