Restructure the js app (#243)

* prettier

* linter

* add prettier, and format the code

* remove common, merge it with frontend

* refactor the app

* better lint and code fix

* travis for the frontend app

* travis build script

Signed-off-by: Daniel Maricic <daniel@woss.io>

* lint and build

* update the README.md

Signed-off-by: Daniel Maricic <daniel@woss.io>

* change the commands to reflect refactor

Signed-off-by: Daniel Maricic <daniel@woss.io>

* prettier and tslint are friends

Signed-off-by: Daniel Maricic <daniel@woss.io>

* code that wasn't linted properly before

Signed-off-by: Daniel Maricic <daniel@woss.io>

* prettier rc got deleted

* workgin on making the travis pass

Signed-off-by: Daniel Maricic <daniel@woss.io>

* travis build please?

Signed-off-by: Daniel Maricic <daniel@woss.io>

* update readme.md

Signed-off-by: Daniel Maricic <daniel@woss.io>

* dockerfile deleted from fronted - out of scope

Signed-off-by: Daniel Maricic <daniel@woss.io>

* remove

Signed-off-by: Daniel Maricic <daniel@woss.io>

* tsconfig

Signed-off-by: Daniel Maricic <daniel@woss.io>

* found the reason why EOL wasn't happening

Signed-off-by: Daniel Maricic <daniel@woss.io>

* type for the event in the ConnectionInput

as suggested

* strictnullCheck to true

* noImplicitAny

* noUnusedParams

* AfgHandling

* update

* fix Location.tsx

* Few minor fixes

* remove connection input and revert to original

* esnext fixes the imports for icons and non default `* as `

* update to the tsconfig.test.json don't use commonjs please

* fixed wrong comment for TIMEOUT_BASE

* return totem.svg and type decraration of maybe

Signed-off-by: Daniel Maricic <daniel@woss.io>

Co-authored-by: Will <w.kopp@kigroup.de>
This commit is contained in:
Daniel Maricic
2020-04-06 15:38:45 +02:00
committed by GitHub
parent 20a0283380
commit bb8e804567
322 changed files with 10896 additions and 10602 deletions
+5 -1
View File
@@ -1,4 +1,8 @@
[*.{js,ts,tsx,json,css}] root = true
[*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
+2 -2
View File
@@ -7,10 +7,10 @@ backend/target
node_modules node_modules
# testing # testing
packages/*/coverage coverage
# production # production
packages/*/build build
# misc # misc
.DS_Store .DS_Store
+4 -3
View File
@@ -30,6 +30,7 @@ before_script:
script: script:
- yarn - yarn
- yarn check:all - cd frontend && yarn
- yarn build:all - yarn test
- cd backend && cargo test - yarn check
- yarn build
+21 -9
View File
@@ -1,16 +1,18 @@
# Polkadot Telemetry # Polkadot Telemetry
## Getting Started ## Getting Started
To run the backend, you will need `cargo` to build the binary. We recommend using [`rustup`](https://rustup.rs/). To run the backend, you will need `cargo` to build the binary. We recommend using [`rustup`](https://rustup.rs/).
To run the frontend make sure to grab the latest stable version of node and install dependencies before doing anything: To run the frontend make sure to grab the latest stable version of node and install dependencies before doing anything:
``` ```sh
nvm install stable nvm install stable
yarn yarn
``` ```
### Terminal 1 - Backend ### Terminal 1 - Backend
``` ```
cd backend cd backend
cargo build --release cargo build --release
@@ -20,35 +22,45 @@ cargo build --release
By default, telemetry will listen on the local interface only (127.0.0.1) on port 8000. You may change both those values: By default, telemetry will listen on the local interface only (127.0.0.1) on port 8000. You may change both those values:
Use another port: Use another port:
``` ```
PORT=8123 telemetry PORT=8123 telemetry
``` ```
You may also change the the listening interface. This is especially required if you are using docker: You may also change the the listening interface. This is especially required if you are using docker:
``` ```
BIND=0.0.0.0 telemetry BIND=0.0.0.0 telemetry
``` ```
### Terminal 2 - Frontend ### Terminal 2 - Frontend
```
yarn start:frontend ```sh
cd frontend
yarn
yarn start
``` ```
### Terminal 3 - Node ### Terminal 3 - Node
Follow up installation instructions from the [Polkadot repo](https://github.com/paritytech/polkadot) Follow up installation instructions from the [Polkadot repo](https://github.com/paritytech/polkadot)
``` ```sh
./target/release/polkadot --dev --telemetry-url ws://localhost:8000/submit ./target/release/polkadot --dev --telemetry-url ws://localhost:8000/submit
``` ```
### Run via Docker ### Run via Docker
To run via docker make sure that you have Docker Desktop To run via docker make sure that you have Docker Desktop
- If you dont you can download for you OS here [Docker Desktop](https://www.docker.com/products/docker-desktop)
``` - If you dont you can download for you OS here [Docker Desktop](https://www.docker.com/products/docker-desktop)
```sh
docker-compose up --build -d docker-compose up --build -d
``` ```
- -d stands for detach, if you would like to see logs i recommend using [Kitmatic](https://kitematic.com/) or dont use the -d
- --build will build the images and rebuild, but this is not required everytime - -d stands for detach, if you would like to see logs i recommend using [Kitmatic](https://kitematic.com/) or don't use the -d
- If you want to makes UI changes, there is no need to rebuild the image as the files are being copied in via volumes. - --build will build the images and rebuild, but this is not required every time
- If you want to makes UI changes, there is no need to rebuild the image as the files are being copied in via volumes.
Now navigate to localhost:3000 in your browser to view the app. Now navigate to localhost:3000 in your browser to view the app.
@@ -25,8 +25,8 @@
"blakejs": "^1.1.0", "blakejs": "^1.1.0",
"husky": "^4.2.3", "husky": "^4.2.3",
"lint-staged": "^10.1.0", "lint-staged": "^10.1.0",
"react": "16.4.0", "react": "^16.13.1",
"react-dom": "16.4.2", "react-dom": "^16.13.1",
"react-measure": "^2.3.0", "react-measure": "^2.3.0",
"react-scripts-ts": "2.17.0", "react-scripts-ts": "2.17.0",
"react-svg": "^4.1.1", "react-svg": "^4.1.1",
@@ -56,10 +56,10 @@
"jest": "^23.6.0", "jest": "^23.6.0",
"jest-localstorage-mock": "^2.2.0", "jest-localstorage-mock": "^2.2.0",
"mock-socket": "^8.0.2", "mock-socket": "^8.0.2",
"prettier": "^2.0.2",
"react-test-renderer": "^16.5.2", "react-test-renderer": "^16.5.2",
"ts-jest": "^23.10.2", "ts-jest": "^23.10.2",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"prettier": "^2.0.2",
"tslint-plugin-prettier": "^2.3.0", "tslint-plugin-prettier": "^2.3.0",
"typescript": "^2.9.2" "typescript": "^2.9.2"
}, },
@@ -1,4 +1,4 @@
import { Types } from '@dotstats/common'; import { Types } from './common';
import { State, UpdateBound } from './state'; import { State, UpdateBound } from './state';
// Number of blocks which are kept in memory // Number of blocks which are kept in memory
@@ -21,7 +21,7 @@ export class AfgHandling {
this.getState().authoritySetId != null && this.getState().authoritySetId != null &&
authoritySetId !== this.getState().authoritySetId authoritySetId !== this.getState().authoritySetId
) { ) {
// the visualization is restarted when we receive a new auhority set // the visualization is restarted when we receive a new authority set
this.updateState({ this.updateState({
authoritySetId, authoritySetId,
authorities, authorities,
@@ -113,7 +113,6 @@ export class AfgHandling {
public receivedPre( public receivedPre(
addr: Types.Address, addr: Types.Address,
height: Types.BlockNumber, height: Types.BlockNumber,
hash: Types.BlockHash,
voter: Types.Address, voter: Types.Address,
what: string what: string
) { ) {
@@ -136,7 +135,7 @@ export class AfgHandling {
// a vote on all preceding blocks. This function marks // a vote on all preceding blocks. This function marks
// the preceding blocks as implicitly voted on and stores // the preceding blocks as implicitly voted on and stores
// a pointer to the block which contains the explicit vote. // a pointer to the block which contains the explicit vote.
const op = (i: Types.BlockNumber, index: number): boolean => { const op = (index: number): boolean => {
const consensusDetail = state.consensusInfo[index][1][addr][voter]; const consensusDetail = state.consensusInfo[index][1][addr][voter];
if ( if (
what === 'prevote' && what === 'prevote' &&
@@ -192,7 +191,7 @@ export class AfgHandling {
const item: Types.ConsensusItem = [height, consensusView]; const item: Types.ConsensusItem = [height, consensusView];
const insertPos = consensusInfo.findIndex( const insertPos = consensusInfo.findIndex(
([elHeight, elView]) => elHeight < height ([elHeight]) => elHeight < height
); );
if (insertPos >= 0) { if (insertPos >= 0) {
consensusInfo.splice(insertPos, 0, item); consensusInfo.splice(insertPos, 0, item);
@@ -6,8 +6,8 @@
.App-no-telemetry { .App-no-telemetry {
width: 100vw; width: 100vw;
height: 100vh; /* height: 100vh; */
line-height: 80vh; /* line-height: 80vh; */
font-size: 56px; font-size: 56px;
font-weight: 100; font-weight: 100;
text-align: center; text-align: center;
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, SortedCollection, Maybe, Compare } from '@dotstats/common'; import { Types, SortedCollection, Maybe, Compare } from './common';
import { AllChains, Chains, Chain, Ago, OfflineIndicator } from './components'; import { AllChains, Chains, Chain, Ago, OfflineIndicator } from './components';
import { Row, Column } from './components/List'; import { Row, Column } from './components/List';
import { Connection } from './Connection'; import { Connection } from './Connection';
@@ -189,7 +189,7 @@ export default class App extends React.Component<{}, State> {
}); });
}; };
private onHashChange = (event: Event) => { private onHashChange = () => {
const { tab = '' } = getHashData(); const { tab = '' } = getHashData();
this.setState({ tab }); this.setState({ tab });
@@ -1,11 +1,4 @@
import { import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from './common';
VERSION,
timestamp,
FeedMessage,
Types,
Maybe,
sleep,
} from '@dotstats/common';
import { import {
State, State,
Update, Update,
@@ -17,10 +10,9 @@ import {
import { PersistentSet } from './persist'; import { PersistentSet } from './persist';
import { getHashData, setHashData } from './utils'; import { getHashData, setHashData } from './utils';
import { AfgHandling } from './AfgHandling'; import { AfgHandling } from './AfgHandling';
import { VIS_AUTHORITIES_LIMIT } from '../../frontend/src/components/Consensus'; import { VIS_AUTHORITIES_LIMIT } from './components/Consensus';
import { Column } from './components/List'; import { Column } from './components/List';
import { ACTIONS } from './common/feed';
const { Actions } = FeedMessage;
const TIMEOUT_BASE = (1000 * 5) as Types.Milliseconds; // 5 seconds const TIMEOUT_BASE = (1000 * 5) as Types.Milliseconds; // 5 seconds
const TIMEOUT_MAX = (1000 * 60 * 5) as Types.Milliseconds; // 5 minutes const TIMEOUT_MAX = (1000 * 60 * 5) as Types.Milliseconds; // 5 minutes
@@ -40,8 +32,6 @@ export class Connection {
? `wss://${window.location.hostname}/feed/` ? `wss://${window.location.hostname}/feed/`
: `ws://127.0.0.1:8000/feed`; : `ws://127.0.0.1:8000/feed`;
// private static readonly address = 'wss://telemetry.polkadot.io/feed/';
private static async socket(): Promise<WebSocket> { private static async socket(): Promise<WebSocket> {
let socket = await Connection.trySocket(); let socket = await Connection.trySocket();
let timeout = TIMEOUT_BASE; let timeout = TIMEOUT_BASE;
@@ -158,7 +148,7 @@ export class Connection {
for (const message of messages) { for (const message of messages) {
switch (message.action) { switch (message.action) {
case Actions.FeedVersion: { case ACTIONS.FeedVersion: {
if (message.payload !== VERSION) { if (message.payload !== VERSION) {
this.state = this.update({ status: 'upgrade-requested' }); this.state = this.update({ status: 'upgrade-requested' });
this.clean(); this.clean();
@@ -172,7 +162,7 @@ export class Connection {
break; break;
} }
case Actions.BestBlock: { case ACTIONS.BestBlock: {
const [best, blockTimestamp, blockAverage] = message.payload; const [best, blockTimestamp, blockAverage] = message.payload;
nodes.mutEach((node) => node.newBestBlock()); nodes.mutEach((node) => node.newBestBlock());
@@ -182,7 +172,7 @@ export class Connection {
break; break;
} }
case Actions.BestFinalized: { case ACTIONS.BestFinalized: {
const [finalized /*, hash */] = message.payload; const [finalized /*, hash */] = message.payload;
this.state = this.update({ finalized }); this.state = this.update({ finalized });
@@ -190,7 +180,7 @@ export class Connection {
break; break;
} }
case Actions.AddedNode: { case ACTIONS.AddedNode: {
const [ const [
id, id,
nodeDetails, nodeDetails,
@@ -219,7 +209,7 @@ export class Connection {
break; break;
} }
case Actions.RemovedNode: { case ACTIONS.RemovedNode: {
const id = message.payload; const id = message.payload;
nodes.remove(id); nodes.remove(id);
@@ -227,7 +217,7 @@ export class Connection {
break; break;
} }
case Actions.StaleNode: { case ACTIONS.StaleNode: {
const id = message.payload; const id = message.payload;
nodes.mutAndSort(id, (node) => node.setStale(true)); nodes.mutAndSort(id, (node) => node.setStale(true));
@@ -235,7 +225,7 @@ export class Connection {
break; break;
} }
case Actions.LocatedNode: { case ACTIONS.LocatedNode: {
const [id, lat, lon, city] = message.payload; const [id, lat, lon, city] = message.payload;
nodes.mutAndMaybeSort( nodes.mutAndMaybeSort(
@@ -247,7 +237,7 @@ export class Connection {
break; break;
} }
case Actions.ImportedBlock: { case ACTIONS.ImportedBlock: {
const [id, blockDetails] = message.payload; const [id, blockDetails] = message.payload;
nodes.mutAndSort(id, (node) => node.updateBlock(blockDetails)); nodes.mutAndSort(id, (node) => node.updateBlock(blockDetails));
@@ -255,7 +245,7 @@ export class Connection {
break; break;
} }
case Actions.FinalizedBlock: { case ACTIONS.FinalizedBlock: {
const [id, height, hash] = message.payload; const [id, height, hash] = message.payload;
nodes.mutAndMaybeSort( nodes.mutAndMaybeSort(
@@ -268,7 +258,7 @@ export class Connection {
break; break;
} }
case Actions.NodeStats: { case ACTIONS.NodeStats: {
const [id, nodeStats] = message.payload; const [id, nodeStats] = message.payload;
nodes.mutAndMaybeSort( nodes.mutAndMaybeSort(
@@ -280,7 +270,7 @@ export class Connection {
break; break;
} }
case Actions.NodeHardware: { case ACTIONS.NodeHardware: {
const [id, nodeHardware] = message.payload; const [id, nodeHardware] = message.payload;
nodes.mutAndMaybeSort( nodes.mutAndMaybeSort(
@@ -295,7 +285,7 @@ export class Connection {
break; break;
} }
case Actions.NodeIO: { case ACTIONS.NodeIO: {
const [id, nodeIO] = message.payload; const [id, nodeIO] = message.payload;
nodes.mutAndMaybeSort( nodes.mutAndMaybeSort(
@@ -310,7 +300,7 @@ export class Connection {
break; break;
} }
case Actions.TimeSync: { case ACTIONS.TimeSync: {
this.state = this.update({ this.state = this.update({
timeDiff: (timestamp() - message.payload) as Types.Milliseconds, timeDiff: (timestamp() - message.payload) as Types.Milliseconds,
}); });
@@ -318,7 +308,7 @@ export class Connection {
break; break;
} }
case Actions.AddedChain: { case ACTIONS.AddedChain: {
const [label, nodeCount] = message.payload; const [label, nodeCount] = message.payload;
const chain = chains.get(label); const chain = chains.get(label);
@@ -333,7 +323,7 @@ export class Connection {
break; break;
} }
case Actions.RemovedChain: { case ACTIONS.RemovedChain: {
chains.delete(message.payload); chains.delete(message.payload);
if (this.state.subscribed === message.payload) { if (this.state.subscribed === message.payload) {
@@ -345,7 +335,7 @@ export class Connection {
break; break;
} }
case Actions.SubscribedTo: { case ACTIONS.SubscribedTo: {
nodes.clear(); nodes.clear();
this.state = this.update({ subscribed: message.payload, nodes }); this.state = this.update({ subscribed: message.payload, nodes });
@@ -353,7 +343,7 @@ export class Connection {
break; break;
} }
case Actions.UnsubscribedFrom: { case ACTIONS.UnsubscribedFrom: {
if (this.state.subscribed === message.payload) { if (this.state.subscribed === message.payload) {
nodes.clear(); nodes.clear();
@@ -363,13 +353,13 @@ export class Connection {
break; break;
} }
case Actions.Pong: { case ACTIONS.Pong: {
this.pong(Number(message.payload)); this.pong(Number(message.payload));
break; break;
} }
case Actions.AfgFinalized: { case ACTIONS.AfgFinalized: {
const [nodeAddress, finalizedNumber, finalizedHash] = message.payload; const [nodeAddress, finalizedNumber, finalizedHash] = message.payload;
const no = parseInt(String(finalizedNumber), 10) as Types.BlockNumber; const no = parseInt(String(finalizedNumber), 10) as Types.BlockNumber;
afg.receivedFinalized(nodeAddress, no, finalizedHash); afg.receivedFinalized(nodeAddress, no, finalizedHash);
@@ -377,23 +367,23 @@ export class Connection {
break; break;
} }
case Actions.AfgReceivedPrevote: { case ACTIONS.AfgReceivedPrevote: {
const [nodeAddress, blockNumber, blockHash, voter] = message.payload; const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber; const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
afg.receivedPre(nodeAddress, no, blockHash, voter, 'prevote'); afg.receivedPre(nodeAddress, no, voter, 'prevote');
break; break;
} }
case Actions.AfgReceivedPrecommit: { case ACTIONS.AfgReceivedPrecommit: {
const [nodeAddress, blockNumber, blockHash, voter] = message.payload; const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber; const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
afg.receivedPre(nodeAddress, no, blockHash, voter, 'precommit'); afg.receivedPre(nodeAddress, no, voter, 'precommit');
break; break;
} }
case Actions.AfgAuthoritySet: { case ACTIONS.AfgAuthoritySet: {
const [authoritySetId, authorities] = message.payload; const [authoritySetId, authorities] = message.payload;
afg.receivedAuthoritySet(authoritySetId, authorities); afg.receivedAuthoritySet(authoritySetId, authorities);

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 324 KiB

@@ -12,7 +12,11 @@ export type Compare<T> = (a: T, b: T) => number;
* *
* @return {number} insertion index * @return {number} insertion index
*/ */
export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): number { export function sortedInsert<T>(
item: T,
into: Array<T>,
compare: Compare<T>
): number {
if (into.length === 0) { if (into.length === 0) {
into.push(item); into.push(item);
@@ -23,7 +27,7 @@ export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): n
let max = into.length - 1; let max = into.length - 1;
while (min !== max) { while (min !== max) {
const guess = (min + max) / 2 | 0; const guess = ((min + max) / 2) | 0;
if (compare(item, into[guess]) < 0) { if (compare(item, into[guess]) < 0) {
max = Math.max(min, guess - 1); max = Math.max(min, guess - 1);
@@ -50,7 +54,11 @@ export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): n
* *
* @return {number} index of the element, `-1` if not found * @return {number} index of the element, `-1` if not found
*/ */
export function sortedIndexOf<T>(item: T, within: Array<T>, compare: Compare<T>): number { export function sortedIndexOf<T>(
item: T,
within: Array<T>,
compare: Compare<T>
): number {
if (within.length === 0) { if (within.length === 0) {
return -1; return -1;
} }
@@ -59,7 +67,7 @@ export function sortedIndexOf<T>(item: T, within: Array<T>, compare: Compare<T>)
let max = within.length - 1; let max = within.length - 1;
while (min !== max) { while (min !== max) {
let guess = (min + max) / 2 | 0; const guess = ((min + max) / 2) | 0;
const other = within[guess]; const other = within[guess];
if (item === other) { if (item === other) {
@@ -113,7 +121,9 @@ export class SortedCollection<Item extends { id: number }> {
public add(item: Item) { public add(item: Item) {
if (this.map.length <= item.id) { if (this.map.length <= item.id) {
// Grow map if item.id would be out of scope // Grow map if item.id would be out of scope
this.map = this.map.concat(Array<Maybe<Item>>(Math.max(10, 1 + item.id - this.map.length))); this.map = this.map.concat(
Array<Maybe<Item>>(Math.max(10, 1 + item.id - this.map.length))
);
} }
// Remove old item if overriding // Remove old item if overriding
@@ -178,7 +188,11 @@ export class SortedCollection<Item extends { id: number }> {
} }
} }
public mutAndMaybeSort(id: number, mutator: (item: Item) => void, sort: boolean) { public mutAndMaybeSort(
id: number,
mutator: (item: Item) => void,
sort: boolean
) {
if (sort) { if (sort) {
this.mutAndSort(id, mutator); this.mutAndSort(id, mutator);
} else { } else {
@@ -1,4 +1,4 @@
import { Opaque, Maybe } from './helpers'; import { Maybe } from './helpers';
import { stringify, parse, Stringified } from './stringify'; import { stringify, parse, Stringified } from './stringify';
import { import {
FeedVersion, FeedVersion,
@@ -22,32 +22,32 @@ import {
AuthoritySetInfo, AuthoritySetInfo,
} from './types'; } from './types';
export const Actions = { export const ACTIONS = {
FeedVersion : 0x00 as 0x00, FeedVersion: 0x00 as 0x00,
BestBlock : 0x01 as 0x01, BestBlock: 0x01 as 0x01,
BestFinalized : 0x02 as 0x02, BestFinalized: 0x02 as 0x02,
AddedNode : 0x03 as 0x03, AddedNode: 0x03 as 0x03,
RemovedNode : 0x04 as 0x04, RemovedNode: 0x04 as 0x04,
LocatedNode : 0x05 as 0x05, LocatedNode: 0x05 as 0x05,
ImportedBlock : 0x06 as 0x06, ImportedBlock: 0x06 as 0x06,
FinalizedBlock : 0x07 as 0x07, FinalizedBlock: 0x07 as 0x07,
NodeStats : 0x08 as 0x08, NodeStats: 0x08 as 0x08,
NodeHardware : 0x09 as 0x09, NodeHardware: 0x09 as 0x09,
TimeSync : 0x0A as 0x0A, TimeSync: 0x0a as 0x0a,
AddedChain : 0x0B as 0x0B, AddedChain: 0x0b as 0x0b,
RemovedChain : 0x0C as 0x0C, RemovedChain: 0x0c as 0x0c,
SubscribedTo : 0x0D as 0x0D, SubscribedTo: 0x0d as 0x0d,
UnsubscribedFrom : 0x0E as 0x0E, UnsubscribedFrom: 0x0e as 0x0e,
Pong : 0x0F as 0x0F, Pong: 0x0f as 0x0f,
AfgFinalized : 0x10 as 0x10, AfgFinalized: 0x10 as 0x10,
AfgReceivedPrevote : 0x11 as 0x11, AfgReceivedPrevote: 0x11 as 0x11,
AfgReceivedPrecommit : 0x12 as 0x12, AfgReceivedPrecommit: 0x12 as 0x12,
AfgAuthoritySet : 0x13 as 0x13, AfgAuthoritySet: 0x13 as 0x13,
StaleNode : 0x14 as 0x14, StaleNode: 0x14 as 0x14,
NodeIO : 0x15 as 0x15, NodeIO: 0x15 as 0x15,
}; };
export type Action = typeof Actions[keyof typeof Actions]; export type Action = typeof ACTIONS[keyof typeof ACTIONS];
export type Payload = Message['payload']; export type Payload = Message['payload'];
export namespace Variants { export namespace Variants {
@@ -56,112 +56,121 @@ export namespace Variants {
} }
export interface FeedVersionMessage extends MessageBase { export interface FeedVersionMessage extends MessageBase {
action: typeof Actions.FeedVersion; action: typeof ACTIONS.FeedVersion;
payload: FeedVersion; payload: FeedVersion;
} }
export interface BestBlockMessage extends MessageBase { export interface BestBlockMessage extends MessageBase {
action: typeof Actions.BestBlock; action: typeof ACTIONS.BestBlock;
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>]; payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
} }
export interface BestFinalizedBlockMessage extends MessageBase { export interface BestFinalizedBlockMessage extends MessageBase {
action: typeof Actions.BestFinalized; action: typeof ACTIONS.BestFinalized;
payload: [BlockNumber, BlockHash]; payload: [BlockNumber, BlockHash];
} }
export interface AddedNodeMessage extends MessageBase { export interface AddedNodeMessage extends MessageBase {
action: typeof Actions.AddedNode; action: typeof ACTIONS.AddedNode;
payload: [NodeId, NodeDetails, NodeStats, NodeIO, NodeHardware, BlockDetails, Maybe<NodeLocation>, Timestamp]; payload: [
NodeId,
NodeDetails,
NodeStats,
NodeIO,
NodeHardware,
BlockDetails,
Maybe<NodeLocation>,
Timestamp
];
} }
export interface RemovedNodeMessage extends MessageBase { export interface RemovedNodeMessage extends MessageBase {
action: typeof Actions.RemovedNode; action: typeof ACTIONS.RemovedNode;
payload: NodeId; payload: NodeId;
} }
export interface LocatedNodeMessage extends MessageBase { export interface LocatedNodeMessage extends MessageBase {
action: typeof Actions.LocatedNode; action: typeof ACTIONS.LocatedNode;
payload: [NodeId, Latitude, Longitude, City]; payload: [NodeId, Latitude, Longitude, City];
} }
export interface ImportedBlockMessage extends MessageBase { export interface ImportedBlockMessage extends MessageBase {
action: typeof Actions.ImportedBlock; action: typeof ACTIONS.ImportedBlock;
payload: [NodeId, BlockDetails]; payload: [NodeId, BlockDetails];
} }
export interface FinalizedBlockMessage extends MessageBase { export interface FinalizedBlockMessage extends MessageBase {
action: typeof Actions.FinalizedBlock; action: typeof ACTIONS.FinalizedBlock;
payload: [NodeId, BlockNumber, BlockHash]; payload: [NodeId, BlockNumber, BlockHash];
} }
export interface NodeStatsMessage extends MessageBase { export interface NodeStatsMessage extends MessageBase {
action: typeof Actions.NodeStats; action: typeof ACTIONS.NodeStats;
payload: [NodeId, NodeStats]; payload: [NodeId, NodeStats];
} }
export interface NodeHardwareMessage extends MessageBase { export interface NodeHardwareMessage extends MessageBase {
action: typeof Actions.NodeHardware; action: typeof ACTIONS.NodeHardware;
payload: [NodeId, NodeHardware]; payload: [NodeId, NodeHardware];
} }
export interface NodeIOMessage extends MessageBase { export interface NodeIOMessage extends MessageBase {
action: typeof Actions.NodeIO; action: typeof ACTIONS.NodeIO;
payload: [NodeId, NodeIO]; payload: [NodeId, NodeIO];
} }
export interface TimeSyncMessage extends MessageBase { export interface TimeSyncMessage extends MessageBase {
action: typeof Actions.TimeSync; action: typeof ACTIONS.TimeSync;
payload: Timestamp; payload: Timestamp;
} }
export interface AddedChainMessage extends MessageBase { export interface AddedChainMessage extends MessageBase {
action: typeof Actions.AddedChain; action: typeof ACTIONS.AddedChain;
payload: [ChainLabel, NodeCount]; payload: [ChainLabel, NodeCount];
} }
export interface RemovedChainMessage extends MessageBase { export interface RemovedChainMessage extends MessageBase {
action: typeof Actions.RemovedChain; action: typeof ACTIONS.RemovedChain;
payload: ChainLabel; payload: ChainLabel;
} }
export interface SubscribedToMessage extends MessageBase { export interface SubscribedToMessage extends MessageBase {
action: typeof Actions.SubscribedTo; action: typeof ACTIONS.SubscribedTo;
payload: ChainLabel; payload: ChainLabel;
} }
export interface UnsubscribedFromMessage extends MessageBase { export interface UnsubscribedFromMessage extends MessageBase {
action: typeof Actions.UnsubscribedFrom; action: typeof ACTIONS.UnsubscribedFrom;
payload: ChainLabel; payload: ChainLabel;
} }
export interface PongMessage extends MessageBase { export interface PongMessage extends MessageBase {
action: typeof Actions.Pong; action: typeof ACTIONS.Pong;
payload: string; // just echo whatever `ping` sent payload: string; // just echo whatever `ping` sent
} }
export interface AfgFinalizedMessage extends MessageBase { export interface AfgFinalizedMessage extends MessageBase {
action: typeof Actions.AfgFinalized; action: typeof ACTIONS.AfgFinalized;
payload: [Address, BlockNumber, BlockHash]; payload: [Address, BlockNumber, BlockHash];
} }
export interface AfgAuthoritySet extends MessageBase { export interface AfgAuthoritySet extends MessageBase {
action: typeof Actions.AfgAuthoritySet; action: typeof ACTIONS.AfgAuthoritySet;
payload: AuthoritySetInfo; payload: AuthoritySetInfo;
} }
export interface AfgReceivedPrecommit extends MessageBase { export interface AfgReceivedPrecommit extends MessageBase {
action: typeof Actions.AfgReceivedPrecommit; action: typeof ACTIONS.AfgReceivedPrecommit;
payload: [Address, BlockNumber, BlockHash, Address]; payload: [Address, BlockNumber, BlockHash, Address];
} }
export interface AfgReceivedPrevote extends MessageBase { export interface AfgReceivedPrevote extends MessageBase {
action: typeof Actions.AfgReceivedPrevote; action: typeof ACTIONS.AfgReceivedPrevote;
payload: [Address, BlockNumber, BlockHash, Address]; payload: [Address, BlockNumber, BlockHash, Address];
} }
export interface StaleNodeMessage extends MessageBase { export interface StaleNodeMessage extends MessageBase {
action: typeof Actions.StaleNode; action: typeof ACTIONS.StaleNode;
payload: NodeId; payload: NodeId;
} }
} }
@@ -194,7 +203,7 @@ export type Message =
* Data type to be sent to the feed. Passing through strings means we can only serialize once, * 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. * no matter how many feed clients are listening in.
*/ */
export interface SquashedMessages extends Array<Action | Payload> {}; export interface SquashedMessages extends Array<Action | Payload> {}
export type Data = Stringified<SquashedMessages>; export type Data = Stringified<SquashedMessages>;
/** /**
@@ -213,7 +222,7 @@ export function serialize(messages: Array<Message>): Data {
squashed[index++] = action; squashed[index++] = action;
squashed[index++] = payload; squashed[index++] = payload;
}) });
return stringify(squashed); return stringify(squashed);
} }
@@ -231,7 +240,7 @@ export function deserialize(data: Data): Array<Message> {
const messages = new Array<Message>(json.length / 2); const messages = new Array<Message>(json.length / 2);
for (const index of messages.keys()) { for (const index of messages.keys()) {
const [ action, payload ] = json.slice(index * 2); const [action, payload] = json.slice(index * 2);
messages[index] = { action, payload } as Message; messages[index] = { action, payload } as Message;
} }
@@ -4,7 +4,9 @@ import { Milliseconds, Timestamp } from './types';
* PhantomData akin to Rust, because sometimes you need to be smarter than * PhantomData akin to Rust, because sometimes you need to be smarter than
* the compiler. * the compiler.
*/ */
export abstract class PhantomData<P> { public __PHANTOM__: P } export abstract class PhantomData<P> {
public __PHANTOM__: P;
}
/** /**
* Opaque type, similar to `opaque type` in Flow, or new types in Rust/C. * Opaque type, similar to `opaque type` in Flow, or new types in Rust/C.
@@ -109,6 +111,8 @@ export class NumStats<T extends number> {
} }
private nonEmpty(): Readonly<Array<number>> { private nonEmpty(): Readonly<Array<number>> {
return this.index < this.history ? this.stack.slice(0, this.index) : this.stack; return this.index < this.history
? this.stack.slice(0, this.index)
: this.stack;
} }
} }
@@ -9,7 +9,7 @@ export type Id<T> = Opaque<number, T>;
* Higher order function producing new auto-incremented `Id`s. * Higher order function producing new auto-incremented `Id`s.
*/ */
export function idGenerator<I extends Id<any>>(): () => I { export function idGenerator<I extends Id<any>>(): () => I {
let current = 0; let current = 0;
return () => current++ as I; return () => current++ as I;
} }
+84
View File
@@ -0,0 +1,84 @@
export function* map<T, U>(
iter: IterableIterator<T>,
fn: (item: T) => U
): IterableIterator<U> {
for (const item of iter) {
yield fn(item);
}
}
export function* chain<T>(
a: IterableIterator<T>,
b: IterableIterator<T>
): IterableIterator<T> {
yield* a;
yield* b;
}
export function* zip<T, U>(
a: IterableIterator<T>,
b: IterableIterator<U>
): IterableIterator<[T, U]> {
let itemA = a.next();
let itemB = b.next();
while (!itemA.done && !itemB.done) {
yield [itemA.value, itemB.value];
itemA = a.next();
itemB = b.next();
}
}
export function* take<T>(
iter: IterableIterator<T>,
n: number
): IterableIterator<T> {
for (const item of iter) {
if (n-- === 0) {
return;
}
yield item;
}
}
export function skip<T>(
iter: IterableIterator<T>,
n: number
): IterableIterator<T> {
while (n-- !== 0 && !iter.next().done) {}
return iter;
}
export function reduce<T, R>(
iter: IterableIterator<T>,
fn: (accu: R, item: T) => R,
accumulator: R
): R {
for (const item of iter) {
accumulator = fn(accumulator, item);
}
return accumulator;
}
export function join(
iter: IterableIterator<{ toString: () => string }>,
glue: string
): string {
const first = iter.next();
if (first.done) {
return '';
}
let result = first.value.toString();
for (const item of iter) {
result += glue + item;
}
return result;
}
+8
View File
@@ -0,0 +1,8 @@
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>;
@@ -27,26 +27,55 @@ export type BytesPerSecond = Opaque<number, 'BytesPerSecond'>;
export type NetworkId = Opaque<string, 'NetworkId'>; export type NetworkId = Opaque<string, 'NetworkId'>;
export type NetworkState = Opaque<string | object, 'NetworkState'>; export type NetworkState = Opaque<string | object, 'NetworkState'>;
export type BlockDetails = [BlockNumber, BlockHash, Milliseconds, Timestamp, Maybe<PropagationTime>]; export type BlockDetails = [
export type NodeDetails = [NodeName, NodeImplementation, NodeVersion, Maybe<Address>, Maybe<NetworkId>]; BlockNumber,
BlockHash,
Milliseconds,
Timestamp,
Maybe<PropagationTime>
];
export type NodeDetails = [
NodeName,
NodeImplementation,
NodeVersion,
Maybe<Address>,
Maybe<NetworkId>
];
export type NodeStats = [PeerCount, TransactionCount]; export type NodeStats = [PeerCount, TransactionCount];
export type NodeIO = [Array<Bytes>, Array<Bytes>, Array<BytesPerSecond>, Array<BytesPerSecond>]; export type NodeIO = [
export type NodeHardware = [Array<MemoryUse>, Array<CPUUse>, Array<BytesPerSecond>, Array<BytesPerSecond>, Array<Timestamp>]; Array<Bytes>,
Array<Bytes>,
Array<BytesPerSecond>,
Array<BytesPerSecond>
];
export type NodeHardware = [
Array<MemoryUse>,
Array<CPUUse>,
Array<BytesPerSecond>,
Array<BytesPerSecond>,
Array<Timestamp>
];
export type NodeLocation = [Latitude, Longitude, City]; export type NodeLocation = [Latitude, Longitude, City];
export declare type Authority = { export interface Authority {
Address: Address, Address: Address;
NodeId: Maybe<NodeId>, NodeId: Maybe<NodeId>;
Name: Maybe<NodeName>, Name: Maybe<NodeName>;
}; }
export declare type Authorities = Array<Address>; export declare type Authorities = Array<Address>;
export declare type AuthoritySetId = Opaque<number, 'AuthoritySetId'>; export declare type AuthoritySetId = Opaque<number, 'AuthoritySetId'>;
export declare type AuthoritySetInfo = [AuthoritySetId, Authorities, Address, BlockNumber, BlockHash]; export declare type AuthoritySetInfo = [
AuthoritySetId,
Authorities,
Address,
BlockNumber,
BlockHash
];
export declare type ConsensusItem = [BlockNumber, ConsensusView]; export declare type ConsensusItem = [BlockNumber, ConsensusView];
export declare type ConsensusInfo = Array<ConsensusItem>; export declare type ConsensusInfo = Array<ConsensusItem>;
export declare type ConsensusView = Map<Address, ConsensusState>; export declare type ConsensusView = Map<Address, ConsensusState>;
export declare type ConsensusState = Map<Address, ConsensusDetail>; export declare type ConsensusState = Map<Address, ConsensusDetail>;
export declare type ConsensusDetail = { export interface ConsensusDetail {
Precommit: Precommit; Precommit: Precommit;
ImplicitPrecommit: ImplicitPrecommit; ImplicitPrecommit: ImplicitPrecommit;
Prevote: Prevote; Prevote: Prevote;
@@ -56,7 +85,7 @@ export declare type ConsensusDetail = {
ImplicitFinalized: Finalized; ImplicitFinalized: Finalized;
FinalizedHash: BlockHash; FinalizedHash: BlockHash;
FinalizedHeight: BlockNumber; FinalizedHeight: BlockNumber;
}; }
export declare type Precommit = Opaque<boolean, 'Precommit'>; export declare type Precommit = Opaque<boolean, 'Precommit'>;
export declare type Prevote = Opaque<boolean, 'Prevote'>; export declare type Prevote = Opaque<boolean, 'Prevote'>;
export declare type Finalized = Opaque<boolean, 'Finalized'>; export declare type Finalized = Opaque<boolean, 'Finalized'>;
@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import './Tile.css'; import './Tile.css';
import { timestamp, Types } from '@dotstats/common'; import { timestamp, Types } from '../common';
export namespace Ago { export namespace Ago {
export interface Props { export interface Props {
@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Connection } from '../Connection'; import { Connection } from '../Connection';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../common';
import { ChainData } from '../state'; import { ChainData } from '../state';
import './AllChains.css'; import './AllChains.css';
@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Connection } from '../../Connection'; import { Connection } from '../../Connection';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { State as AppState } from '../../state'; import { State as AppState } from '../../state';
import { formatNumber, secondsWithPrecision, getHashData } from '../../utils'; import { formatNumber, secondsWithPrecision, getHashData } from '../../utils';
import { Tab } from './'; import { Tab } from './';
@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { Connection } from '../Connection'; import { Connection } from '../Connection';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../common';
import { ChainData } from '../state'; import { ChainData } from '../state';
import githubIcon from '../icons/mark-github.svg'; import githubIcon from '../icons/mark-github.svg';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { Connection } from '../../Connection'; import { Connection } from '../../Connection';
import Measure, { BoundingRect, ContentRect } from 'react-measure'; import Measure, { BoundingRect, ContentRect } from 'react-measure';
@@ -357,7 +357,7 @@ export class Consensus extends React.Component<Consensus.Props, {}> {
private getSmallRow(blocks: Types.ConsensusInfo) { private getSmallRow(blocks: Types.ConsensusInfo) {
const smallBlockSizeChanged = ( const smallBlockSizeChanged = (
isFirstBlock: boolean, _isFirstBlock: boolean,
rect: BoundingRect rect: BoundingRect
) => { ) => {
if (this.smallBlocksSizeDetected(this.state)) { if (this.smallBlocksSizeDetected(this.state)) {
@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import Measure, { BoundingRect, ContentRect } from 'react-measure'; import Measure, { BoundingRect, ContentRect } from 'react-measure';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { Icon, Tooltip, PolkadotIcon } from '../'; import { Icon, Tooltip, PolkadotIcon } from '../';
import Jdenticon from './Jdenticon'; import Jdenticon from './Jdenticon';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Maybe } from '@dotstats/common'; import { Maybe } from '../common';
import { Node } from '../state'; import { Node } from '../state';
import { Icon } from './'; import { Icon } from './';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe, timestamp } from '@dotstats/common'; import { Types, Maybe, timestamp } from '../../common';
import { State, Node } from '../../state'; import { State, Node } from '../../state';
import { Truncate } from './'; import { Truncate } from './';
import { Ago, Icon, Tooltip, Sparkline, PolkadotIcon } from '../'; import { Ago, Icon, Tooltip, Sparkline, PolkadotIcon } from '../';
@@ -59,6 +59,7 @@ import dothereumIcon from '../../icons/dothereum.svg';
import katalchainIcon from '../../icons/katalchain.svg'; import katalchainIcon from '../../icons/katalchain.svg';
import bifrostIcon from '../../icons/bifrost.svg'; import bifrostIcon from '../../icons/bifrost.svg';
import totemIcon from '../../icons/totem.svg'; import totemIcon from '../../icons/totem.svg';
import unknownImplementationIcon from '../../icons/question-solid.svg'; import unknownImplementationIcon from '../../icons/question-solid.svg';
const ICONS = { const ICONS = {
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Maybe } from '@dotstats/common'; import { Maybe } from '../../common';
import { Column } from './'; import { Column } from './';
import { Icon, Tooltip } from '../'; import { Icon, Tooltip } from '../';
import { Persistent } from '../../persist'; import { Persistent } from '../../persist';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { Filter } from '../'; import { Filter } from '../';
import { State as AppState, Node } from '../../state'; import { State as AppState, Node } from '../../state';
import { Row } from './'; import { Row } from './';
@@ -86,7 +86,7 @@ export class List extends React.Component<List.Props, {}> {
<React.Fragment> <React.Fragment>
<div className="List" style={{ height }}> <div className="List" style={{ height }}>
<table> <table>
<Row.Header columns={selectedColumns} sortBy={sortBy} /> <Row.HEADER columns={selectedColumns} sortBy={sortBy} />
<tbody style={{ transform }}> <tbody style={{ transform }}>
{nodes.map((node) => ( {nodes.map((node) => (
<Row <Row
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { Node } from '../../state'; import { Node } from '../../state';
import { Persistent, PersistentSet } from '../../persist'; import { Persistent, PersistentSet } from '../../persist';
import { HeaderCell, Column } from './'; import { HeaderCell, Column } from './';
@@ -51,7 +51,7 @@ export class Row extends React.Component<Row.Props, Row.State> {
Column.NETWORK_STATE, Column.NETWORK_STATE,
]; ];
public static Header = (props: HeaderProps) => { public static HEADER = (props: HeaderProps) => {
const { columns, sortBy } = props; const { columns, sortBy } = props;
const last = columns.length - 1; const last = columns.length - 1;
@@ -91,7 +91,7 @@ export class Location extends React.Component<Location.Props, Location.State> {
city, city,
} = this.props.node; } = this.props.node;
let validatorRow = null; let validatorRow = <div />;
if (validator) { if (validator) {
validatorRow = ( validatorRow = (
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../../common';
import { Filter } from '../'; import { Filter } from '../';
import { State as AppState, Node } from '../../state'; import { State as AppState, Node } from '../../state';
import { Location } from './'; import { Location } from './';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Maybe } from '@dotstats/common'; import { Maybe } from '../../common';
import { State as AppState } from '../../state'; import { State as AppState } from '../../state';
import { Setting } from './'; import { Setting } from './';
import { Row } from '../List'; import { Row } from '../List';
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Types, Maybe } from '@dotstats/common'; import { Types, Maybe } from '../common';
import sparkline from '@fnando/sparkline'; import sparkline from '@fnando/sparkline';
import { Tooltip } from './'; import { Tooltip } from './';
@@ -79,7 +79,7 @@ export class Sparkline extends React.Component<Sparkline.Props, {}> {
}; };
private onMouseMove = ( private onMouseMove = (
event: MouseEvent, _event: MouseEvent,
data: { value: number; index: number } data: { value: number; index: number }
) => { ) => {
const { format, stamps } = this.props; const { format, stamps } = this.props;

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 265 B

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 297 B

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 395 B

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 250 B

Before

Width:  |  Height:  |  Size: 397 B

After

Width:  |  Height:  |  Size: 397 B

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 283 B

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 686 B

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 588 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

Some files were not shown because too many files have changed in this diff Show More