Ui 2 pubsub components (#6152)
* Remove Application.orig from merge * Disable i18n extraction for development * Retrieve blockNumber via pubsub * Chain via pubsub * StatusIndicator with health * WIP * WIP * s/BlockStatus/BlockNumber/ * Adjust BlockNumber display * Cleanup debug * Fix statusbar indicator * NetPeers component * Add BlockTimestamp * Export statics on observer * Cleanup debug logs * Update references
This commit is contained in:
@@ -68,10 +68,12 @@
|
||||
height: .4em;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
&.needsAttention {
|
||||
height: .6em;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
&.ok {
|
||||
height: 1em;
|
||||
}
|
||||
@@ -80,9 +82,11 @@
|
||||
&.bad > .bar.active {
|
||||
background-color: #c00;
|
||||
}
|
||||
|
||||
&.ok > .bar.active {
|
||||
background-color: #080;
|
||||
}
|
||||
|
||||
&.needsAttention > .bar.active {
|
||||
background-color: #dc0;
|
||||
}
|
||||
|
||||
@@ -14,58 +14,86 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import Store from './store';
|
||||
|
||||
import styles from './statusIndicator.css';
|
||||
|
||||
const statuses = ['bad', 'needsAttention', 'ok'];
|
||||
|
||||
export default class StatusIndicator extends Component {
|
||||
static propTypes = {
|
||||
type: PropTypes.oneOf(['radial', 'signal']),
|
||||
id: PropTypes.string.isRequired,
|
||||
status: PropTypes.oneOf(statuses).isRequired,
|
||||
title: PropTypes.arrayOf(PropTypes.node),
|
||||
tooltipPlacement: PropTypes.oneOf(['left', 'top', 'bottom', 'right'])
|
||||
};
|
||||
function StatusIndicator ({ id, status, title = [], tooltipPlacement, type = 'signal' }, { api }) {
|
||||
const store = Store.get(api);
|
||||
const checkStatus = status || store.overall.status;
|
||||
const message = title.length
|
||||
? title
|
||||
: store.overall.message;
|
||||
|
||||
static defaultProps = {
|
||||
type: 'signal',
|
||||
title: []
|
||||
};
|
||||
return (
|
||||
<span className={ styles.status }>
|
||||
<span
|
||||
className={ `${styles[type]} ${styles[checkStatus]}` }
|
||||
data-tip={ message.length }
|
||||
data-for={ `status-${id}` }
|
||||
data-place={ tooltipPlacement }
|
||||
data-effect='solid'
|
||||
>
|
||||
{
|
||||
type === 'signal'
|
||||
? statuses.map((signal) => {
|
||||
const index = statuses.indexOf(checkStatus);
|
||||
const isActive = statuses.indexOf(signal) <= index;
|
||||
|
||||
render () {
|
||||
const { id, status, title, type, tooltipPlacement } = this.props;
|
||||
const tooltip = title.find(x => !x.isEmpty) ? (
|
||||
<ReactTooltip id={ `status-${id}` }>
|
||||
{ title.map(x => (<div key={ x }>{ x }</div>)) }
|
||||
</ReactTooltip>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<span className={ styles.status }>
|
||||
<span className={ `${styles[type]} ${styles[status]}` }
|
||||
data-tip={ title.length }
|
||||
data-for={ `status-${id}` }
|
||||
data-place={ tooltipPlacement }
|
||||
data-effect='solid'
|
||||
>
|
||||
{ type === 'signal' && statuses.map(this.renderBar) }
|
||||
</span>
|
||||
{tooltip}
|
||||
return (
|
||||
<span
|
||||
key={ signal }
|
||||
className={ `${styles.bar} ${styles[signal]} ${isActive ? styles.active : ''}` }
|
||||
/>
|
||||
);
|
||||
})
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderBar = (signal) => {
|
||||
const idx = statuses.indexOf(this.props.status);
|
||||
const isActive = statuses.indexOf(signal) <= idx;
|
||||
const activeClass = isActive ? styles.active : '';
|
||||
|
||||
return (
|
||||
<span key={ signal } className={ `${styles.bar} ${styles[signal]} ${activeClass}` } />
|
||||
);
|
||||
}
|
||||
{
|
||||
message.find((x) => !x.isEmpty)
|
||||
? (
|
||||
<ReactTooltip id={ `status-${id}` }>
|
||||
{
|
||||
message.map((x) => (
|
||||
<div key={ x }>
|
||||
{ x }
|
||||
</div>)
|
||||
)
|
||||
}
|
||||
</ReactTooltip>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
StatusIndicator.propTypes = {
|
||||
type: PropTypes.oneOf([
|
||||
'radial', 'signal'
|
||||
]),
|
||||
id: PropTypes.string.isRequired,
|
||||
status: PropTypes.oneOf(statuses),
|
||||
title: PropTypes.arrayOf(PropTypes.node),
|
||||
tooltipPlacement: PropTypes.oneOf([
|
||||
'left', 'top', 'bottom', 'right'
|
||||
])
|
||||
};
|
||||
|
||||
StatusIndicator.contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const ObserverComponent = observer(StatusIndicator);
|
||||
|
||||
ObserverComponent.Store = Store;
|
||||
|
||||
export default ObserverComponent;
|
||||
|
||||
106
js/packages/ui/StatusIndicator/store.js
Normal file
106
js/packages/ui/StatusIndicator/store.js
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity 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 General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, computed, observable } from 'mobx';
|
||||
|
||||
const STATUS_OK = 'ok';
|
||||
const STATUS_WARN = 'needsAttention';
|
||||
const STATUS_BAD = 'bad';
|
||||
const EMPTY_OVERALL = { message: [], status: STATUS_BAD };
|
||||
|
||||
export default class Store {
|
||||
@observable _health = null;
|
||||
|
||||
constructor (api) {
|
||||
this._api = api;
|
||||
|
||||
setInterval(this.fetchHealth, 2500);
|
||||
}
|
||||
|
||||
@computed get health () {
|
||||
return this._health
|
||||
? this._health
|
||||
: {};
|
||||
}
|
||||
|
||||
@computed get overall () {
|
||||
return this._health
|
||||
? this._health.overall
|
||||
: EMPTY_OVERALL;
|
||||
}
|
||||
|
||||
fetchHealth = () => {
|
||||
// Support Parity-Extension.
|
||||
const uiUrl = this._api.transport.uiUrlWithProtocol || '';
|
||||
|
||||
return fetch(`${uiUrl}/api/health`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.catch(() => {
|
||||
return null;
|
||||
})
|
||||
.then(this.setHealth);
|
||||
}
|
||||
|
||||
_overallStatus = (health) => {
|
||||
const all = [health.peers, health.sync, health.time].filter(x => x);
|
||||
const statuses = all.map(x => x.status);
|
||||
const bad = statuses.find(x => x === STATUS_BAD);
|
||||
const needsAttention = statuses.find(x => x === STATUS_WARN);
|
||||
const message = all.map(x => x.message).filter(x => x);
|
||||
|
||||
if (all.length) {
|
||||
return {
|
||||
status: bad || needsAttention || STATUS_OK,
|
||||
message
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: STATUS_BAD,
|
||||
message: ['Unable to fetch node health.']
|
||||
};
|
||||
}
|
||||
|
||||
@action setHealth = (health) => {
|
||||
if (!health) {
|
||||
this._health = null;
|
||||
return;
|
||||
}
|
||||
|
||||
health.peers = health.peers || {};
|
||||
health.sync = health.sync || {};
|
||||
health.time = health.time || {};
|
||||
health.overall = this._overallStatus(health);
|
||||
|
||||
this._health = health;
|
||||
}
|
||||
|
||||
static instance = null;
|
||||
|
||||
static get (api) {
|
||||
if (!Store.instance) {
|
||||
Store.instance = new Store(api);
|
||||
}
|
||||
|
||||
return Store.instance;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user