Skip to content
Snippets Groups Projects
Unverified Commit b457ebff authored by Levente Pap's avatar Levente Pap Committed by GitHub
Browse files

Update Autopeering Visualizer (#457)

* Update Autopeering Visualizer

 * Display online nodes in Wordcloud
 * Refactor NodeView layout
 * Display only shortened nodeID-s (first 8 chars)

* Refactor

* Frontend updates, remove wordcloud

 * wordcloud consumes too much resources, removed for smooth
   graph updates.
 * Add more colors to NodeView

* Update packr2
parent ef89a201
No related branches found
No related tags found
No related merge requests found
......@@ -2,9 +2,8 @@ import * as React from 'react';
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import {inject, observer} from "mobx-react";
import AutopeeringStore from "app/stores/AutopeeringStore";
import AutopeeringStore, {shortenedIDCharCount} from "app/stores/AutopeeringStore";
import {Col} from "react-bootstrap";
import ListGroup from "react-bootstrap/ListGroup";
import Badge from "react-bootstrap/Badge";
import FormControl from "react-bootstrap/FormControl";
import InputGroup from "react-bootstrap/InputGroup";
......@@ -28,23 +27,51 @@ export class NodeView extends React.Component<Props, any> {
};
return (
<div>
<Badge pill style={{background: "#cb4b16", color: "white"}}>
Selected Node
</Badge>
<em>{this.props.autopeeringStore.selectedNode}</em>
<br/>
<Badge pill style={{background: "#1c8d7f", color: "white"}}>
Incoming Neighbors ({this.props.autopeeringStore.selectedNodeInNeighbors.size.toString()})
</Badge>
<ul>
{this.props.autopeeringStore.inNeighborList}
</ul>
<Badge pill style={{background: "#336db5", color: "white"}}>
Outgoing Neighbors ({this.props.autopeeringStore.selectedNodeOutNeighbors.size.toString()})
</Badge>
<ul>
{this.props.autopeeringStore.outNeighborList}
</ul>
<Row style={{paddingBottom: 10}}>
<Col style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Badge pill style={{background: "#cb4b16", color: "white"}}>
Selected Node
</Badge>
</Col>
</Row>
<Row style={{paddingBottom: 20}}>
<Col style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Button style={{fontSize: 14, backgroundColor: "#cb4b16"}} variant="danger">
{this.props.autopeeringStore.selectedNode.slice(0,shortenedIDCharCount)}
</Button>
</Col>
</Row>
<Row style={{paddingBottom: 10}}>
<Col style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Badge pill style={{background: "#1c8d7f", color: "white"}}>
Incoming Neighbors ({this.props.autopeeringStore.selectedNodeInNeighbors.size.toString()})
</Badge>
</Col>
<Col style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Badge pill style={{background: "#336db5", color: "white"}}>
Outgoing Neighbors ({this.props.autopeeringStore.selectedNodeOutNeighbors.size.toString()})
</Badge>
</Col>
</Row>
<Row style={{paddingBottom: 20}}>
<Col>
<ul style={{marginLeft: 25, listStylePosition: 'inside'}}>
{this.props.autopeeringStore.inNeighborList}
</ul>
</Col>
<Col>
<ul style={{marginLeft: 25, listStylePosition: 'inside'}}>
{this.props.autopeeringStore.outNeighborList}
</ul>
</Col>
</Row>
<Row>
<Col style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Button style={{fontSize: 11}} variant="info" onClick={this.props.autopeeringStore.clearNodeSelection}>
Clear Selection
</Button>
</Col>
</Row>
</div>
);
}
......@@ -72,7 +99,7 @@ export class Autopeering extends React.Component<Props, any> {
<Container>
<Row className={"mb-1"}>
<Col xs={6} style={{paddingBottom: 15}}>
<Col xs={6}>
<Row className={"mb-1"}>
<h3>Autopeering Visualizer</h3>
</Row>
......@@ -88,7 +115,6 @@ export class Autopeering extends React.Component<Props, any> {
</Badge>
</Col>
</Row>
Online nodes
<InputGroup className="mb-1" size="sm">
<InputGroup.Prepend>
......@@ -102,16 +128,12 @@ export class Autopeering extends React.Component<Props, any> {
aria-label="node-search" onKeyUp={this.updateSearch}
aria-describedby="node-search"
/>
<Button style={{fontSize: 14}} variant="secondary" onClick={this.props.autopeeringStore.clearSelection}>
Clear Selection
</Button>
</InputGroup>
<ListGroup style={{maxHeight: 220, overflow: 'auto'}}>
<div style={{height: 220, overflow: 'auto'}}>
{nodeListView}
</ListGroup>
</div>
</Col>
<Col xs={6} style={{height: 385, overflow:'auto'}}>
<Col xs={6} style={{height: 352.5, overflow:'auto'}}>
<NodeView></NodeView>
</Col>
</Row>
......@@ -123,8 +145,6 @@ export class Autopeering extends React.Component<Props, any> {
background: "#202126",
borderRadius: 20,
}} id={"visualizer"}/>
</Container>
);
}
......
......@@ -2,8 +2,7 @@ import {RouterStore} from "mobx-react-router";
import {action, computed, observable, ObservableMap, ObservableSet} from "mobx";
import {connectWebSocket, registerHandler, WSMsgType} from "app/misc/WS";
import {default as Viva} from 'vivagraphjs';
import * as React from "react";
import ListGroupItem from "react-bootstrap/ListGroupItem";
import * as React from "react";;
import Button from "react-bootstrap/Button";
......@@ -48,6 +47,8 @@ const VERTEX_SIZE_ACTIVE = 24;
const VERTEX_SIZE_CONNECTED = 18;
const statusWebSocketPath = "/ws";
export const shortenedIDCharCount = 8;
export class AutopeeringStore {
routerStore: RouterStore;
......@@ -89,6 +90,16 @@ export class AutopeeringStore {
() => this.updateWebSocketConnected(false))
}
// derive the full node ID based on the shortened nodeID (first shortenedIDCharCount chars)
getFullNodeID = (shortNodeID: string) => {
for(let fullNodeID of this.nodes.values()){
if (fullNodeID.startsWith(shortNodeID)) {
return fullNodeID;
}
}
return "";
};
@action
updateWebSocketConnected = (connected: boolean) => this.websocketConnected = connected;
......@@ -219,7 +230,7 @@ export class AutopeeringStore {
this.connections.add(msg.source + msg.target);
// Update neighbors map
if (this.neighbors.get(msg.source) == undefined) {
if (this.neighbors.get(msg.source) === undefined) {
let neighbors = new Neighbors();
neighbors.out.add(msg.target);
this.neighbors.set(msg.source, neighbors);
......@@ -227,7 +238,7 @@ export class AutopeeringStore {
this.neighbors.get(msg.source).out.add(msg.target);
}
if (this.neighbors.get(msg.target) == undefined) {
if (this.neighbors.get(msg.target) === undefined) {
let neighbors = new Neighbors();
neighbors.in.add(msg.source);
this.neighbors.set(msg.target, neighbors);
......@@ -356,11 +367,25 @@ export class AutopeeringStore {
// handlers for frontend events //
// updates the currently selected node
@action
updateSelectedNode = (node: string) => {
this.selectedNode = node;
// get node incoming neighbors
if (!this.nodes.has(this.selectedNode)) {
console.log("Selected node not found (%s)", this.selectedNode);
}
this.selectedNodeInNeighbors = this.neighbors.get(this.selectedNode).in;
this.selectedNodeOutNeighbors = this.neighbors.get(this.selectedNode).out;
this.selectionActive = true;
this.showHighlight();
}
// handles graph event of mouse entering a node
@action
handleGraphNodeOnHover = (node) => {
// when node is already selected
if (this.selectionActive && this.selectedNode == node.id) {
if (this.selectionActive && this.selectedNode === node.id) {
return;
}
......@@ -368,33 +393,31 @@ export class AutopeeringStore {
if (this.selectionActive) {
this.resetPreviousColors(true);
}
this.selectedNode = node.id;
// get node incoming neighbors
if (!this.nodes.has(this.selectedNode)) {
console.log("Selected node not found (%s)", this.selectedNode);
}
this.selectedNodeInNeighbors = this.neighbors.get(this.selectedNode).in;
this.selectedNodeOutNeighbors = this.neighbors.get(this.selectedNode).out;
this.selectionActive = true;
this.showHighlight();
this.updateSelectedNode(node.id);
}
// handles graph event of mouse leaving a node
@action
handleGraphNodeOnHoverLeave = (node) => {
this.clearSelection();
this.clearNodeSelection();
return;
}
// handles click on a node in list
// handles click on a node button
@action
handleNodeListOnClick = (e) => {
handleNodeButtonOnClick = (e) => {
// find node based on the first 8 characters
let clickedNode = this.getFullNodeID(e.target.innerHTML)
this.handleNodeSelection(clickedNode);
}
// checks whether selection is already active, then updates selected node
@action
handleNodeSelection = (clickedNode: string) => {
if (this.selectionActive) {
if (this.selectedNode == e.target.innerHTML) {
if (this.selectedNode === clickedNode) {
// Disable selection on second click when clicked on the same node
this.clearSelection();
this.clearNodeSelection();
return;
} else {
// we clicked on a different node
......@@ -403,21 +426,12 @@ export class AutopeeringStore {
this.resetPreviousColors(true, true);
}
}
this.selectedNode = e.target.innerHTML;
// get node incoming neighbors
if (!this.nodes.has(this.selectedNode)) {
console.log("Selected node not found (%s)", this.selectedNode);
}
this.selectedNodeInNeighbors = this.neighbors.get(this.selectedNode).in;
this.selectedNodeOutNeighbors = this.neighbors.get(this.selectedNode).out;
this.selectionActive = true;
this.showHighlight();
this.updateSelectedNode(clickedNode);
}
// handles clearing the node selection
@action
clearSelection = () => {
clearNodeSelection = () => {
this.resetPreviousColors();
this.selectedNode = null;
this.selectedNodeInNeighbors = null;
......@@ -430,9 +444,8 @@ export class AutopeeringStore {
@computed
get nodeListView(){
let nodeList = [];
let results = null;
if (this.search == "") {
if (this.search === "") {
results = this.nodes;
} else {
results = new Set();
......@@ -442,17 +455,17 @@ export class AutopeeringStore {
}
})
}
let buttons = [];
results.forEach((nodeID) => {
nodeList.push(
<ListGroupItem key={nodeID} style={{padding: 0}}>
<Button style={{fontSize: 12}} variant="outline-dark" onClick={this.handleNodeListOnClick}>
{nodeID}
</Button>
</ListGroupItem>
let shortID = nodeID.slice(0,shortenedIDCharCount);
buttons.push(
<Button style={{fontSize: 12, margin: 2.5}} variant="outline-dark" onClick={this.handleNodeButtonOnClick}>
{shortID}
</Button>
)
})
return nodeList
return buttons
}
@computed
......@@ -461,8 +474,13 @@ export class AutopeeringStore {
this.selectedNodeInNeighbors.forEach((inNeighborID) => {
inNeighbors.push(
<li key={inNeighborID}>
<Button style={{fontSize: 12}} variant="outline-dark" onClick={this.handleNodeListOnClick}>
{inNeighborID}
<Button style={{
fontSize: 12,
background: "#1c8d7f",
borderColor: 'white',
color: 'white',
}} variant="light" onClick={this.handleNodeButtonOnClick}>
{inNeighborID.slice(0,shortenedIDCharCount)}
</Button>
</li>
......@@ -477,8 +495,13 @@ export class AutopeeringStore {
this.selectedNodeOutNeighbors.forEach((outNeighborID) => {
outNeighbors.push(
<li key={outNeighborID}>
<Button style={{fontSize: 12}} variant="outline-dark" onClick={this.handleNodeListOnClick}>
{outNeighborID}
<Button style={{
fontSize: 12,
background: "#336db5",
borderColor: 'white',
color: 'white',
}} variant="light" onClick={this.handleNodeButtonOnClick}>
{outNeighborID.slice(0,shortenedIDCharCount)}
</Button>
</li>
)
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment