From d299e2103aa740f98688a99f6659c84ac38fa804 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz <1096222+maciejhirsz@users.noreply.github.com> Date: Fri, 5 Oct 2018 18:21:43 +0200 Subject: [PATCH] Render individual node changes on demand (#76) --- packages/frontend/src/Connection.ts | 55 ++++++++++++------- packages/frontend/src/components/Node/Row.tsx | 28 +++++++++- packages/frontend/src/state.ts | 31 +++++++++++ 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/packages/frontend/src/Connection.ts b/packages/frontend/src/Connection.ts index 78a4a57..b6fd08f 100644 --- a/packages/frontend/src/Connection.ts +++ b/packages/frontend/src/Connection.ts @@ -85,7 +85,10 @@ export class Connection { const { nodes, chains } = this.state; let { sortedNodes } = this.state; - messages: for (const message of messages) { + // TODO: boolean flags are code smell, find a cleaner way to do this + let dirty = false; + + for (const message of messages) { switch (message.action) { case Actions.FeedVersion: { if (message.payload !== VERSION) { @@ -98,17 +101,17 @@ export class Connection { return; } - continue messages; + break; } case Actions.BestBlock: { const [best, blockTimestamp, blockAverage] = message.payload; - nodes.forEach((node) => node.propagationTime = null); + nodes.forEach((node) => node.newBestBlock()); this.state = this.update({ best, blockTimestamp, blockAverage }); - continue messages; + break; } case Actions.AddedNode: { @@ -124,6 +127,8 @@ export class Connection { sortedNodes = Array.from(nodes.values()).sort(Node.compare); } + dirty = true; + break; } @@ -140,11 +145,11 @@ export class Connection { console.error('Node count in sorted array is wrong!'); sortedNodes = Array.from(nodes.values()).sort(Node.compare); } - - break; } - continue messages; + dirty = true; + + break; } case Actions.LocatedNode: { @@ -152,12 +157,10 @@ export class Connection { const node = nodes.get(id); if (!node) { - continue messages; + break; } - node.lat = lat; - node.lon = lon; - node.city = city; + node.updateLocation([lat, lon, city]); break; } @@ -167,12 +170,14 @@ export class Connection { const node = nodes.get(id); if (!node) { - continue messages; + break; } node.updateBlock(blockDetails); sortedNodes = sortedNodes.sort(Node.compare); + dirty = true; + break; } @@ -181,7 +186,7 @@ export class Connection { const node = nodes.get(id); if (!node) { - return; + break; } node.updateStats(nodeStats); @@ -207,13 +212,15 @@ export class Connection { timeDiff: (timestamp() - message.payload) as Types.Milliseconds }); - continue messages; + break; } case Actions.AddedChain: { const [label, nodeCount] = message.payload; chains.set(label, nodeCount); + dirty = true; + break; } @@ -224,10 +231,10 @@ export class Connection { nodes.clear(); sortedNodes = []; this.state = this.update({ subscribed: null, nodes, chains, sortedNodes }); - - continue messages; } + dirty = true; + break; } @@ -237,7 +244,9 @@ export class Connection { this.state = this.update({ subscribed: message.payload, nodes, sortedNodes }); - continue messages; + dirty = true; + + break; } case Actions.UnsubscribedFrom: { @@ -247,22 +256,26 @@ export class Connection { this.state = this.update({ subscribed: null, nodes, sortedNodes }); } - continue messages; + dirty = true; + + break; } case Actions.Pong: { this.pong(Number(message.payload)); - continue messages; + break; } default: { - continue messages; + break; } } } - this.state = this.update({ nodes, chains, sortedNodes }); + if (dirty) { + this.state = this.update({ nodes, chains, sortedNodes }); + } this.autoSubscribe(); } diff --git a/packages/frontend/src/components/Node/Row.tsx b/packages/frontend/src/components/Node/Row.tsx index 576f229..7a3f309 100644 --- a/packages/frontend/src/components/Node/Row.tsx +++ b/packages/frontend/src/components/Node/Row.tsx @@ -33,6 +33,10 @@ interface RowProps { pins: PersistentSet; }; +interface RowState { + update: number; +}; + interface HeaderProps { settings: AppState.Settings; }; @@ -88,7 +92,7 @@ function formatCPU(cpu: number, stamp: Maybe): string { return `${cpu.toFixed(fractionDigits)}%${ago}`; } -export default class Row extends React.Component { +export default class Row extends React.Component { public static readonly columns: Column[] = [ { label: 'Node', @@ -236,6 +240,24 @@ export default class Row extends React.Component { ) } + public state = { update: 0 }; + + public componentDidMount() { + const { node } = this.props; + + node.subscribe(this.onUpdate); + } + + public componentWillUnmount() { + const { node } = this.props; + + node.unsubscribe(this.onUpdate); + } + + public shouldComponentUpdate(nextProps: RowProps, nextState: RowState): boolean { + return this.props.node.id !== nextProps.node.id || this.state.update !== nextState.update; + } + public render() { const { node, settings } = this.props; @@ -269,4 +291,8 @@ export default class Row extends React.Component { pins.add(node.name); } } + + private onUpdate = () => { + this.setState({ update: this.state.update + 1 }); + } } diff --git a/packages/frontend/src/state.ts b/packages/frontend/src/state.ts index c0f92ba..dc55a57 100644 --- a/packages/frontend/src/state.ts +++ b/packages/frontend/src/state.ts @@ -41,6 +41,8 @@ export class Node { public lon: Maybe; public city: Maybe; + private readonly subscribtions = new Set<(node: Node) => void>(); + constructor( pinned: boolean, id: Types.NodeId, @@ -74,6 +76,8 @@ export class Node { this.peers = peers; this.txs = txs; + + this.trigger(); } public updateHardware(hardware: Types.NodeHardware) { @@ -82,6 +86,8 @@ export class Node { this.mem = mem; this.cpu = cpu; this.chartstamps = chartstamps; + + this.trigger(); } public updateBlock(block: Types.BlockDetails) { @@ -92,6 +98,8 @@ export class Node { this.blockTime = blockTime; this.blockTimestamp = blockTimestamp; this.propagationTime = propagationTime; + + this.trigger(); } public updateLocation(location: Types.NodeLocation) { @@ -100,6 +108,29 @@ export class Node { this.lat = lat; this.lon = lon; this.city = city; + + this.trigger(); + } + + public newBestBlock() { + if (this.propagationTime != null) { + this.propagationTime = null; + this.trigger(); + } + } + + public subscribe(handler: (node: Node) => void) { + this.subscribtions.add(handler); + } + + public unsubscribe(handler: (node: Node) => void) { + this.subscribtions.delete(handler); + } + + private trigger() { + for (const handler of this.subscribtions.values()) { + handler(this); + } } }