github.com/rymuff/go-ethereum@v1.9.7/dashboard/assets/components/Footer.jsx (about)

     1  // @flow
     2  
     3  // Copyright 2017 The go-ethereum Authors
     4  // This file is part of the go-ethereum library.
     5  //
     6  // The go-ethereum library is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Lesser General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // (at your option) any later version.
    10  //
    11  // The go-ethereum library is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    14  // GNU Lesser General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Lesser General Public License
    17  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    18  
    19  import React, {Component} from 'react';
    20  
    21  import withStyles from '@material-ui/core/styles/withStyles';
    22  import Typography from '@material-ui/core/Typography';
    23  import Grid from '@material-ui/core/Grid';
    24  import ResponsiveContainer from 'recharts/es6/component/ResponsiveContainer';
    25  import AreaChart from 'recharts/es6/chart/AreaChart';
    26  import Area from 'recharts/es6/cartesian/Area';
    27  import ReferenceLine from 'recharts/es6/cartesian/ReferenceLine';
    28  import Label from 'recharts/es6/component/Label';
    29  import Tooltip from 'recharts/es6/component/Tooltip';
    30  
    31  import ChartRow from 'ChartRow';
    32  import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from 'CustomTooltip';
    33  import {chartStrokeWidth, styles as commonStyles} from '../common';
    34  import type {General, System} from '../types/content';
    35  
    36  const FOOTER_SYNC_ID = 'footerSyncId';
    37  
    38  const CPU     = 'cpu';
    39  const MEMORY  = 'memory';
    40  const DISK    = 'disk';
    41  const TRAFFIC = 'traffic';
    42  
    43  const TOP = 'Top';
    44  const BOTTOM = 'Bottom';
    45  
    46  const cpuLabelTop = 'Process load';
    47  const cpuLabelBottom = 'System load';
    48  const memoryLabelTop = 'Active memory';
    49  const memoryLabelBottom = 'Virtual memory';
    50  const diskLabelTop = 'Disk read';
    51  const diskLabelBottom = 'Disk write';
    52  const trafficLabelTop = 'Download';
    53  const trafficLabelBottom = 'Upload';
    54  
    55  // styles contains the constant styles of the component.
    56  const styles = {
    57  	footer: {
    58  		maxWidth: '100%',
    59  		flexWrap: 'nowrap',
    60  		margin:   0,
    61  	},
    62  	chartRowWrapper: {
    63  		height:  '100%',
    64  		padding: 0,
    65  	},
    66  	doubleChartWrapper: {
    67  		height: '100%',
    68  		width:  '99%',
    69  	},
    70  	link: {
    71  		color:          'inherit',
    72  		textDecoration: 'none',
    73  	},
    74  };
    75  
    76  // themeStyles returns the styles generated from the theme for the component.
    77  const themeStyles: Object = (theme: Object) => ({
    78  	footer: {
    79  		backgroundColor: theme.palette.grey[900],
    80  		color:           theme.palette.getContrastText(theme.palette.grey[900]),
    81  		zIndex:          theme.zIndex.appBar,
    82  		height:          theme.spacing.unit * 10,
    83  	},
    84  });
    85  
    86  export type Props = {
    87  	classes: Object, // injected by withStyles()
    88  	theme: Object,
    89  	general: General,
    90  	system: System,
    91  	shouldUpdate: Object,
    92  };
    93  
    94  type State = {};
    95  
    96  // Footer renders the footer of the dashboard.
    97  class Footer extends Component<Props, State> {
    98  	shouldComponentUpdate(nextProps: Readonly<Props>, nextState: Readonly<State>, nextContext: any) {
    99  		return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
   100  	}
   101  
   102  	// halfHeightChart renders an area chart with half of the height of its parent.
   103  	halfHeightChart = (chartProps, tooltip, areaProps, label, position) => (
   104  		<ResponsiveContainer width='100%' height='50%'>
   105  			<AreaChart {...chartProps}>
   106  				{!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)}
   107  				<Area isAnimationActive={false} strokeWidth={chartStrokeWidth} type='monotone' {...areaProps} />
   108  				<ReferenceLine x={0} strokeWidth={0}>
   109  					<Label fill={areaProps.fill} value={label} position={position} />
   110  				</ReferenceLine>
   111  			</AreaChart>
   112  		</ResponsiveContainer>
   113  	);
   114  
   115  	// doubleChart renders a pair of charts separated by the baseline.
   116  	doubleChart = (syncId, chartKey, topChart, bottomChart) => {
   117  		if (!Array.isArray(topChart.data) || !Array.isArray(bottomChart.data)) {
   118  			return null;
   119  		}
   120  		const topDefault = topChart.default || 0;
   121  		const bottomDefault = bottomChart.default || 0;
   122  		const topKey = `${chartKey}${TOP}`;
   123  		const bottomKey = `${chartKey}${BOTTOM}`;
   124  		const topColor = '#8884d8';
   125  		const bottomColor = '#82ca9d';
   126  
   127  		return (
   128  			<div style={styles.doubleChartWrapper}>
   129  				{this.halfHeightChart(
   130  					{
   131  						syncId,
   132  						data:   topChart.data.map(({value}) => ({[topKey]: value || topDefault})),
   133  						margin: {top: 5, right: 5, bottom: 0, left: 5},
   134  					},
   135  					topChart.tooltip,
   136  					{dataKey: topKey, stroke: topColor, fill: topColor},
   137  					topChart.label,
   138  					'insideBottomLeft',
   139  				)}
   140  				{this.halfHeightChart(
   141  					{
   142  						syncId,
   143  						data:   bottomChart.data.map(({value}) => ({[bottomKey]: -value || -bottomDefault})),
   144  						margin: {top: 0, right: 5, bottom: 5, left: 5},
   145  					},
   146  					bottomChart.tooltip,
   147  					{dataKey: bottomKey, stroke: bottomColor, fill: bottomColor},
   148  					bottomChart.label,
   149  					'insideTopLeft',
   150  				)}
   151  			</div>
   152  		);
   153  	};
   154  
   155  	render() {
   156  		const {general, system} = this.props;
   157  
   158  		return (
   159  			<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
   160  				<Grid item xs style={styles.chartRowWrapper}>
   161  					<ChartRow>
   162  						{this.doubleChart(
   163  							FOOTER_SYNC_ID,
   164  							CPU,
   165  							{data: system.processCPU, tooltip: percentPlotter(cpuLabelTop), label: cpuLabelTop},
   166  							{data: system.systemCPU, tooltip: percentPlotter(cpuLabelBottom, multiplier(-1)), label: cpuLabelBottom},
   167  						)}
   168  						{this.doubleChart(
   169  							FOOTER_SYNC_ID,
   170  							MEMORY,
   171  							{data: system.activeMemory, tooltip: bytePlotter(memoryLabelTop), label: memoryLabelTop},
   172  							{data: system.virtualMemory, tooltip: bytePlotter(memoryLabelBottom, multiplier(-1)), label: memoryLabelBottom},
   173  						)}
   174  						{this.doubleChart(
   175  							FOOTER_SYNC_ID,
   176  							DISK,
   177  							{data: system.diskRead, tooltip: bytePerSecPlotter(diskLabelTop), label: diskLabelTop},
   178  							{data: system.diskWrite, tooltip: bytePerSecPlotter(diskLabelBottom, multiplier(-1)), label: diskLabelBottom},
   179  						)}
   180  						{this.doubleChart(
   181  							FOOTER_SYNC_ID,
   182  							TRAFFIC,
   183  							{data: system.networkIngress, tooltip: bytePerSecPlotter(trafficLabelTop), label: trafficLabelTop},
   184  							{data: system.networkEgress, tooltip: bytePerSecPlotter(trafficLabelBottom, multiplier(-1)), label: trafficLabelBottom},
   185  						)}
   186  					</ChartRow>
   187  				</Grid>
   188  				<Grid item>
   189  					<Typography type='caption' color='inherit'>
   190  						<span style={commonStyles.light}>Geth</span> {general.version}
   191  					</Typography>
   192  					{general.commit && (
   193  						<Typography type='caption' color='inherit'>
   194  							<span style={commonStyles.light}>{'Commit '}</span>
   195  							<a
   196  								href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`}
   197  								target='_blank'
   198  								rel='noopener noreferrer'
   199  								style={styles.link}
   200  							>
   201  								{general.commit.substring(0, 8)}
   202  							</a>
   203  						</Typography>
   204  					)}
   205  				</Grid>
   206  			</Grid>
   207  		);
   208  	}
   209  }
   210  
   211  export default withStyles(themeStyles)(Footer);