mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-06-12 14:41:15 +00:00
Turbo Render (#298)
* More responsive React updates on scroll * `Icon`s now use shadow dom * Faster Sparkline * Recycle table rows * Separate Header from Chain to avoid vdom diffing * Separate THead from Row.HEADER to avoid vdom diffing * Throttle rendering updates on chain tabs, also styles * Minor tweaks and fixes * Created components for all columns * Wrapping up Column refactor * Rename Row--td to Column * Lazy `Ago` * Update styles for faster layouting * Minor cleanup * Fix Connection * Use shadow DOM in `PolkadotIcon` * Comments and tweaks for the List component * Faster Tooltip and Truncate * Minor tweaks * Tooltiped columns can now be copied * Future-proof Connection * Remove the <div> wrapper from Icon * Fix dash on missing graph data * Clean up some SVGs * Cleanup and comments * Localize the use of `previousKeys` to `recalculateKeys` * Custom appState disjoint from React component state * Make appState and appUpdate refs readonly * Cleanup
This commit is contained in:
+78
-77
@@ -1,18 +1,21 @@
|
||||
import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from './common';
|
||||
import {
|
||||
State,
|
||||
Update,
|
||||
Node,
|
||||
UpdateBound,
|
||||
ChainData,
|
||||
PINNED_CHAINS,
|
||||
} from './state';
|
||||
import { State, Update, Node, ChainData, PINNED_CHAINS } from './state';
|
||||
import { PersistentSet } from './persist';
|
||||
import { getHashData, setHashData } from './utils';
|
||||
import { AfgHandling } from './AfgHandling';
|
||||
import { VIS_AUTHORITIES_LIMIT } from './components/Consensus';
|
||||
import { Column } from './components/List';
|
||||
import { ACTIONS } from './common/feed';
|
||||
import {
|
||||
Column,
|
||||
LocationColumn,
|
||||
PeersColumn,
|
||||
TxsColumn,
|
||||
FinalizedBlockColumn,
|
||||
FinalizedHashColumn,
|
||||
UploadColumn,
|
||||
DownloadColumn,
|
||||
StateCacheColumn,
|
||||
} from './components/List';
|
||||
|
||||
const TIMEOUT_BASE = (1000 * 5) as Types.Milliseconds; // 5 seconds
|
||||
const TIMEOUT_MAX = (1000 * 60 * 5) as Types.Milliseconds; // 5 minutes
|
||||
@@ -26,9 +29,10 @@ declare global {
|
||||
export class Connection {
|
||||
public static async create(
|
||||
pins: PersistentSet<Types.NodeName>,
|
||||
update: Update
|
||||
appState: Readonly<State>,
|
||||
appUpdate: Update
|
||||
): Promise<Connection> {
|
||||
return new Connection(await Connection.socket(), update, pins);
|
||||
return new Connection(await Connection.socket(), appState, appUpdate, pins);
|
||||
}
|
||||
|
||||
private static readonly utf8decoder = new TextDecoder('utf-8');
|
||||
@@ -102,27 +106,22 @@ export class Connection {
|
||||
private resubscribeTo: Maybe<Types.ChainLabel> = getHashData().chain;
|
||||
// flag whether or not FE should subscribe to consensus updates on reconnect
|
||||
private resubscribeSendFinality: boolean = getHashData().tab === 'consensus';
|
||||
// flag used to throttle DOM updates to window frame rate
|
||||
private isUpdating = false;
|
||||
private socket: WebSocket;
|
||||
private state: Readonly<State>;
|
||||
private readonly update: Update;
|
||||
private readonly pins: PersistentSet<Types.NodeName>;
|
||||
|
||||
constructor(
|
||||
socket: WebSocket,
|
||||
update: Update,
|
||||
pins: PersistentSet<Types.NodeName>
|
||||
private socket: WebSocket,
|
||||
private readonly appState: Readonly<State>,
|
||||
private readonly appUpdate: Update,
|
||||
private readonly pins: PersistentSet<Types.NodeName>
|
||||
) {
|
||||
this.socket = socket;
|
||||
this.update = update;
|
||||
this.pins = pins;
|
||||
this.bindSocket();
|
||||
}
|
||||
|
||||
public subscribe(chain: Types.ChainLabel) {
|
||||
if (this.state.subscribed != null && this.state.subscribed !== chain) {
|
||||
this.state = this.update({
|
||||
if (
|
||||
this.appState.subscribed != null &&
|
||||
this.appState.subscribed !== chain
|
||||
) {
|
||||
this.appUpdate({
|
||||
tab: 'list',
|
||||
});
|
||||
setHashData({ chain, tab: 'list' });
|
||||
@@ -134,7 +133,7 @@ export class Connection {
|
||||
}
|
||||
|
||||
public subscribeConsensus(chain: Types.ChainLabel) {
|
||||
if (this.state.authorities.length <= VIS_AUTHORITIES_LIMIT) {
|
||||
if (this.appState.authorities.length <= VIS_AUTHORITIES_LIMIT) {
|
||||
setHashData({ chain });
|
||||
this.resubscribeSendFinality = true;
|
||||
this.socket.send(`send-finality:${chain}`);
|
||||
@@ -142,7 +141,7 @@ export class Connection {
|
||||
}
|
||||
|
||||
public resetConsensus() {
|
||||
this.state = this.update({
|
||||
this.appUpdate({
|
||||
consensusInfo: new Array() as Types.ConsensusInfo,
|
||||
displayConsensusLoadingScreen: true,
|
||||
authorities: [] as Types.Address[],
|
||||
@@ -156,14 +155,10 @@ export class Connection {
|
||||
}
|
||||
|
||||
public handleMessages = (messages: FeedMessage.Message[]) => {
|
||||
const { nodes, chains, sortBy, selectedColumns } = this.state;
|
||||
const { nodes, chains, sortBy, selectedColumns } = this.appState;
|
||||
const nodesStateRef = nodes.ref;
|
||||
|
||||
const updateState: UpdateBound = (state) => {
|
||||
this.state = this.update(state);
|
||||
};
|
||||
const getState = () => this.state;
|
||||
const afg = new AfgHandling(updateState, getState);
|
||||
const afg = new AfgHandling(this.appUpdate, this.appState);
|
||||
|
||||
let sortByColumn: Maybe<Column> = null;
|
||||
|
||||
@@ -176,13 +171,7 @@ export class Connection {
|
||||
switch (message.action) {
|
||||
case ACTIONS.FeedVersion: {
|
||||
if (message.payload !== VERSION) {
|
||||
this.state = this.update({ status: 'upgrade-requested' });
|
||||
this.clean();
|
||||
|
||||
// Force reload from the server
|
||||
setTimeout(() => window.location.reload(true), 3000);
|
||||
|
||||
return;
|
||||
return this.newVersion();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -193,7 +182,7 @@ export class Connection {
|
||||
|
||||
nodes.mutEach((node) => node.newBestBlock());
|
||||
|
||||
this.state = this.update({ best, blockTimestamp, blockAverage });
|
||||
this.appUpdate({ best, blockTimestamp, blockAverage });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -201,7 +190,7 @@ export class Connection {
|
||||
case ACTIONS.BestFinalized: {
|
||||
const [finalized /*, hash */] = message.payload;
|
||||
|
||||
this.state = this.update({ finalized });
|
||||
this.appUpdate({ finalized });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -257,7 +246,7 @@ export class Connection {
|
||||
nodes.mutAndMaybeSort(
|
||||
id,
|
||||
(node) => node.updateLocation([lat, lon, city]),
|
||||
sortByColumn === Column.LOCATION
|
||||
sortByColumn === LocationColumn
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -277,8 +266,8 @@ export class Connection {
|
||||
nodes.mutAndMaybeSort(
|
||||
id,
|
||||
(node) => node.updateFinalized(height, hash),
|
||||
sortByColumn === Column.FINALIZED ||
|
||||
sortByColumn === Column.FINALIZED_HASH
|
||||
sortByColumn === FinalizedBlockColumn ||
|
||||
sortByColumn === FinalizedHashColumn
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -290,7 +279,7 @@ export class Connection {
|
||||
nodes.mutAndMaybeSort(
|
||||
id,
|
||||
(node) => node.updateStats(nodeStats),
|
||||
sortByColumn === Column.PEERS || sortByColumn === Column.TXS
|
||||
sortByColumn === PeersColumn || sortByColumn === TxsColumn
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -302,7 +291,7 @@ export class Connection {
|
||||
nodes.mutAndMaybeSort(
|
||||
id,
|
||||
(node) => node.updateHardware(nodeHardware),
|
||||
sortByColumn === Column.UPLOAD || sortByColumn === Column.DOWNLOAD
|
||||
sortByColumn === UploadColumn || sortByColumn === DownloadColumn
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -314,14 +303,14 @@ export class Connection {
|
||||
nodes.mutAndMaybeSort(
|
||||
id,
|
||||
(node) => node.updateIO(nodeIO),
|
||||
sortByColumn === Column.STATE_CACHE
|
||||
sortByColumn === StateCacheColumn
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTIONS.TimeSync: {
|
||||
this.state = this.update({
|
||||
this.appUpdate({
|
||||
timeDiff: (timestamp() - message.payload) as Types.Milliseconds,
|
||||
});
|
||||
|
||||
@@ -338,7 +327,7 @@ export class Connection {
|
||||
chains.set(label, { label, nodeCount });
|
||||
}
|
||||
|
||||
this.state = this.update({ chains });
|
||||
this.appUpdate({ chains });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -346,9 +335,9 @@ export class Connection {
|
||||
case ACTIONS.RemovedChain: {
|
||||
chains.delete(message.payload);
|
||||
|
||||
if (this.state.subscribed === message.payload) {
|
||||
if (this.appState.subscribed === message.payload) {
|
||||
nodes.clear();
|
||||
this.state = this.update({ subscribed: null, nodes, chains });
|
||||
this.appUpdate({ subscribed: null, nodes, chains });
|
||||
this.resetConsensus();
|
||||
}
|
||||
|
||||
@@ -358,16 +347,16 @@ export class Connection {
|
||||
case ACTIONS.SubscribedTo: {
|
||||
nodes.clear();
|
||||
|
||||
this.state = this.update({ subscribed: message.payload, nodes });
|
||||
this.appUpdate({ subscribed: message.payload, nodes });
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTIONS.UnsubscribedFrom: {
|
||||
if (this.state.subscribed === message.payload) {
|
||||
if (this.appState.subscribed === message.payload) {
|
||||
nodes.clear();
|
||||
|
||||
this.state = this.update({ subscribed: null, nodes });
|
||||
this.appUpdate({ subscribed: null, nodes });
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -416,12 +405,8 @@ export class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.hasChangedSince(nodesStateRef) && !this.isUpdating) {
|
||||
this.isUpdating = true;
|
||||
window.requestAnimationFrame(() => {
|
||||
this.update({ nodes });
|
||||
this.isUpdating = false;
|
||||
});
|
||||
if (nodes.hasChangedSince(nodesStateRef)) {
|
||||
this.appUpdate({ nodes });
|
||||
}
|
||||
|
||||
this.autoSubscribe();
|
||||
@@ -430,19 +415,19 @@ export class Connection {
|
||||
private bindSocket() {
|
||||
this.ping();
|
||||
|
||||
if (this.state) {
|
||||
const { nodes } = this.state;
|
||||
if (this.appState) {
|
||||
const { nodes } = this.appState;
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
this.state = this.update({
|
||||
this.appUpdate({
|
||||
status: 'online',
|
||||
});
|
||||
|
||||
if (this.state.subscribed) {
|
||||
this.resubscribeTo = this.state.subscribed;
|
||||
this.resubscribeSendFinality = this.state.sendFinality;
|
||||
this.state = this.update({ subscribed: null, sendFinality: false });
|
||||
if (this.appState.subscribed) {
|
||||
this.resubscribeTo = this.appState.subscribed;
|
||||
this.resubscribeSendFinality = this.appState.sendFinality;
|
||||
this.appUpdate({ subscribed: null, sendFinality: false });
|
||||
}
|
||||
|
||||
this.socket.addEventListener('message', this.handleFeedData);
|
||||
@@ -479,8 +464,14 @@ export class Connection {
|
||||
|
||||
const latency = timestamp() - this.pingSent;
|
||||
this.pingSent = null;
|
||||
}
|
||||
|
||||
console.log('latency', latency);
|
||||
private newVersion() {
|
||||
this.appUpdate({ status: 'upgrade-requested' });
|
||||
this.clean();
|
||||
|
||||
// Force reload from the server
|
||||
setTimeout(() => window.location.reload(true), 3000);
|
||||
}
|
||||
|
||||
private clean() {
|
||||
@@ -493,18 +484,28 @@ export class Connection {
|
||||
}
|
||||
|
||||
private handleFeedData = (event: MessageEvent) => {
|
||||
const data =
|
||||
typeof event.data === 'string'
|
||||
? ((event.data as any) as FeedMessage.Data)
|
||||
: ((Connection.utf8decoder.decode(
|
||||
event.data
|
||||
) as any) as FeedMessage.Data);
|
||||
let data: FeedMessage.Data;
|
||||
|
||||
if (typeof event.data === 'string') {
|
||||
data = (event.data as any) as FeedMessage.Data;
|
||||
} else {
|
||||
const u8aData = new Uint8Array(event.data);
|
||||
|
||||
// Future-proofing for when we switch to binary feed
|
||||
if (u8aData[0] === 0x00) {
|
||||
return this.newVersion();
|
||||
}
|
||||
|
||||
const str = Connection.utf8decoder.decode(event.data);
|
||||
|
||||
data = (str as any) as FeedMessage.Data;
|
||||
}
|
||||
|
||||
this.handleMessages(FeedMessage.deserialize(data));
|
||||
};
|
||||
|
||||
private autoSubscribe() {
|
||||
const { subscribed, chains } = this.state;
|
||||
const { subscribed, chains } = this.appState;
|
||||
const { resubscribeTo, resubscribeSendFinality } = this;
|
||||
|
||||
if (subscribed) {
|
||||
@@ -540,7 +541,7 @@ export class Connection {
|
||||
}
|
||||
|
||||
private handleDisconnect = async () => {
|
||||
this.state = this.update({ status: 'offline' });
|
||||
this.appUpdate({ status: 'offline' });
|
||||
this.resetConsensus();
|
||||
this.clean();
|
||||
this.socket.close();
|
||||
|
||||
Reference in New Issue
Block a user