aboutsummaryrefslogtreecommitdiffstats
path: root/dashboard/assets/components/Dashboard.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard/assets/components/Dashboard.jsx')
-rw-r--r--dashboard/assets/components/Dashboard.jsx169
1 files changed, 169 insertions, 0 deletions
diff --git a/dashboard/assets/components/Dashboard.jsx b/dashboard/assets/components/Dashboard.jsx
new file mode 100644
index 000000000..740acf959
--- /dev/null
+++ b/dashboard/assets/components/Dashboard.jsx
@@ -0,0 +1,169 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+import {withStyles} from 'material-ui/styles';
+
+import SideBar from './SideBar.jsx';
+import Header from './Header.jsx';
+import Main from "./Main.jsx";
+import {isNullOrUndefined, LIMIT, TAGS, DATA_KEYS,} from "./Common.jsx";
+
+// Styles for the Dashboard component.
+const styles = theme => ({
+ appFrame: {
+ position: 'relative',
+ display: 'flex',
+ width: '100%',
+ height: '100%',
+ background: theme.palette.background.default,
+ },
+});
+
+// Dashboard is the main component, which renders the whole page, makes connection with the server and listens for messages.
+// When there is an incoming message, updates the page's content correspondingly.
+class Dashboard extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ active: TAGS.home.id, // active menu
+ sideBar: true, // true if the sidebar is opened
+ memory: [],
+ traffic: [],
+ logs: [],
+ shouldUpdate: {},
+ };
+ }
+
+ // componentDidMount initiates the establishment of the first websocket connection after the component is rendered.
+ componentDidMount() {
+ this.reconnect();
+ }
+
+ // reconnect establishes a websocket connection with the server, listens for incoming messages
+ // and tries to reconnect on connection loss.
+ reconnect = () => {
+ const server = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/api");
+
+ server.onmessage = event => {
+ const msg = JSON.parse(event.data);
+ if (isNullOrUndefined(msg)) {
+ return;
+ }
+ this.update(msg);
+ };
+
+ server.onclose = () => {
+ setTimeout(this.reconnect, 3000);
+ };
+ };
+
+ // update analyzes the incoming message, and updates the charts' content correspondingly.
+ update = msg => {
+ console.log(msg);
+ this.setState(prevState => {
+ let newState = [];
+ newState.shouldUpdate = {};
+ const insert = (key, values, limit) => {
+ newState[key] = [...prevState[key], ...values];
+ while (newState[key].length > limit) {
+ newState[key].shift();
+ }
+ newState.shouldUpdate[key] = true;
+ };
+ // (Re)initialize the state with the past data.
+ if (!isNullOrUndefined(msg.history)) {
+ const memory = DATA_KEYS.memory;
+ const traffic = DATA_KEYS.traffic;
+ newState[memory] = [];
+ newState[traffic] = [];
+ if (!isNullOrUndefined(msg.history.memorySamples)) {
+ newState[memory] = msg.history.memorySamples.map(elem => isNullOrUndefined(elem.value) ? 0 : elem.value);
+ while (newState[memory].length > LIMIT.memory) {
+ newState[memory].shift();
+ }
+ newState.shouldUpdate[memory] = true;
+ }
+ if (!isNullOrUndefined(msg.history.trafficSamples)) {
+ newState[traffic] = msg.history.trafficSamples.map(elem => isNullOrUndefined(elem.value) ? 0 : elem.value);
+ while (newState[traffic].length > LIMIT.traffic) {
+ newState[traffic].shift();
+ }
+ newState.shouldUpdate[traffic] = true;
+ }
+ }
+ // Insert the new data samples.
+ if (!isNullOrUndefined(msg.memory)) {
+ insert(DATA_KEYS.memory, [isNullOrUndefined(msg.memory.value) ? 0 : msg.memory.value], LIMIT.memory);
+ }
+ if (!isNullOrUndefined(msg.traffic)) {
+ insert(DATA_KEYS.traffic, [isNullOrUndefined(msg.traffic.value) ? 0 : msg.traffic.value], LIMIT.traffic);
+ }
+ if (!isNullOrUndefined(msg.log)) {
+ insert(DATA_KEYS.logs, [msg.log], LIMIT.log);
+ }
+
+ return newState;
+ });
+ };
+
+ // The change of the active label on the SideBar component will trigger a new render in the Main component.
+ changeContent = active => {
+ this.setState(prevState => prevState.active !== active ? {active: active} : {});
+ };
+
+ openSideBar = () => {
+ this.setState({sideBar: true});
+ };
+
+ closeSideBar = () => {
+ this.setState({sideBar: false});
+ };
+
+ render() {
+ // The classes property is injected by withStyles().
+ const {classes} = this.props;
+
+ return (
+ <div className={classes.appFrame}>
+ <Header
+ opened={this.state.sideBar}
+ open={this.openSideBar}
+ />
+ <SideBar
+ opened={this.state.sideBar}
+ close={this.closeSideBar}
+ changeContent={this.changeContent}
+ />
+ <Main
+ opened={this.state.sideBar}
+ active={this.state.active}
+ memory={this.state.memory}
+ traffic={this.state.traffic}
+ logs={this.state.logs}
+ shouldUpdate={this.state.shouldUpdate}
+ />
+ </div>
+ );
+ }
+}
+
+Dashboard.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(Dashboard);