mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-06-12 18:01:03 +00:00
Allow to pin nodes to top of the list (#48)
* Refactored persistent state a bit * Allow nodes to be pinned to top
This commit is contained in:
@@ -80,7 +80,7 @@
|
||||
|
||||
.Chain-node-list th, .Chain-node-list td {
|
||||
text-align: left;
|
||||
padding: 0.5em 1em;
|
||||
padding: 0.35em 1em;
|
||||
}
|
||||
|
||||
.Chain-settings {
|
||||
|
||||
@@ -2,9 +2,9 @@ import * as React from 'react';
|
||||
import { State as AppState } from '../../state';
|
||||
import { formatNumber, secondsWithPrecision, viewport } from '../../utils';
|
||||
import { Tab } from './';
|
||||
import { Tile, Node, Ago, Option } from '../';
|
||||
import { Tile, Node, Ago, Setting } from '../';
|
||||
import { Types } from '@dotstats/common';
|
||||
import { Persistent } from '../../Persistent';
|
||||
import { PersistentObject, PersistentSet } from '../../persist';
|
||||
|
||||
import blockIcon from '../../icons/package.svg';
|
||||
import blockTimeIcon from '../../icons/history.svg';
|
||||
@@ -24,7 +24,8 @@ export namespace Chain {
|
||||
|
||||
export interface Props {
|
||||
appState: Readonly<AppState>;
|
||||
setSettings: Persistent<AppState.Settings>['set'];
|
||||
settings: PersistentObject<AppState.Settings>;
|
||||
pins: PersistentSet<Types.NodeId>;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
@@ -39,12 +40,16 @@ export namespace Chain {
|
||||
}
|
||||
|
||||
function sortNodes(a: AppState.Node, b: AppState.Node): number {
|
||||
if (a.blockDetails[0] === b.blockDetails[0]) {
|
||||
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
|
||||
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
|
||||
if (a.pinned === b.pinned) {
|
||||
if (a.blockDetails[0] === b.blockDetails[0]) {
|
||||
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
|
||||
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
|
||||
|
||||
// Ascending sort by propagation time
|
||||
return aPropagation - bPropagation;
|
||||
// Ascending sort by propagation time
|
||||
return aPropagation - bPropagation;
|
||||
}
|
||||
} else {
|
||||
return Number(b.pinned) - Number(a.pinned);
|
||||
}
|
||||
|
||||
// Descending sort by block number
|
||||
@@ -124,6 +129,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
|
||||
|
||||
private renderList() {
|
||||
const { settings } = this.props.appState;
|
||||
const { pins } = this.props;
|
||||
|
||||
return (
|
||||
<table className="Chain-node-list">
|
||||
@@ -133,7 +139,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
|
||||
this
|
||||
.nodes()
|
||||
.sort(sortNodes)
|
||||
.map((node) => <Node.Row key={node.id} node={node} settings={settings} />)
|
||||
.map((node) => <Node.Row key={node.id} node={node} settings={settings} pins={pins} />)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -164,7 +170,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
|
||||
}
|
||||
|
||||
private renderSettings() {
|
||||
const { settings } = this.props.appState;
|
||||
const { settings } = this.props;
|
||||
|
||||
return (
|
||||
<div className="Chain-settings">
|
||||
@@ -177,17 +183,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checked = settings[setting];
|
||||
|
||||
const changeSetting = () => {
|
||||
const change = {};
|
||||
|
||||
change[setting] = !settings[setting];
|
||||
|
||||
this.props.setSettings(change);
|
||||
}
|
||||
|
||||
return <Option key={index} onClick={changeSetting} icon={icon} label={label} checked={checked} />;
|
||||
return <Setting key={index} setting={setting} settings={settings} icon={icon} label={label} />;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Chains .Icon {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.Chains-chain {
|
||||
padding: 0 12px;
|
||||
background: #bbb;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Connection } from '../Connection';
|
||||
import { Icon } from './Icon';
|
||||
import { Types, Maybe } from '@dotstats/common';
|
||||
|
||||
import chainIcon from '../icons/link.svg';
|
||||
import githubIcon from '../icons/mark-github.svg';
|
||||
import './Chains.css';
|
||||
|
||||
@@ -24,7 +23,6 @@ export class Chains extends React.Component<Chains.Props, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="Chains">
|
||||
<Icon src={chainIcon} alt="Observed Chain" />
|
||||
{
|
||||
this.chains.map((chain) => this.renderChain(chain))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import './Icon.css';
|
||||
|
||||
export interface Props {
|
||||
src: string;
|
||||
alt: string;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
@@ -12,8 +12,10 @@ export interface Props {
|
||||
export class Icon extends React.Component<{}, Props> {
|
||||
public props: Props;
|
||||
|
||||
public shouldComponentUpdate() {
|
||||
return false;
|
||||
public shouldComponentUpdate(nextProps: Props) {
|
||||
return this.props.src !== nextProps.src
|
||||
|| this.props.alt !== nextProps.alt
|
||||
|| this.props.className !== nextProps.className;
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.Node-Row {
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Node-Row-synced {
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import Identicon from 'polkadot-identicon';
|
||||
import { Types } from '@dotstats/common';
|
||||
import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision } from '../../utils';
|
||||
import { State as AppState } from '../../state';
|
||||
import { PersistentSet } from '../../persist';
|
||||
import { SEMVER_PATTERN } from './';
|
||||
import { Ago, Icon } from '../';
|
||||
|
||||
import pinIcon from '../../icons/pin.svg';
|
||||
import pinOnIcon from '../../icons/check-square-solid.svg';
|
||||
import pinOffIcon from '../../icons/square-solid.svg';
|
||||
import nodeIcon from '../../icons/server.svg';
|
||||
import nodeValidatorIcon from '../../icons/shield.svg';
|
||||
import nodeTypeIcon from '../../icons/terminal.svg';
|
||||
@@ -23,6 +28,7 @@ import './Row.css';
|
||||
interface RowProps {
|
||||
node: AppState.Node;
|
||||
settings: AppState.Settings;
|
||||
pins: PersistentSet<Types.NodeId>;
|
||||
};
|
||||
|
||||
interface HeaderProps {
|
||||
@@ -39,6 +45,12 @@ interface Column {
|
||||
|
||||
export default class Row extends React.Component<RowProps, {}> {
|
||||
public static readonly columns: Column[] = [
|
||||
{
|
||||
label: 'Pin to Top',
|
||||
icon: pinIcon,
|
||||
width: 16,
|
||||
render: ({ pinned }) => <Icon src={pinned ? pinOnIcon : pinOffIcon} />
|
||||
},
|
||||
{
|
||||
label: 'Node',
|
||||
icon: nodeIcon,
|
||||
@@ -93,7 +105,7 @@ export default class Row extends React.Component<RowProps, {}> {
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Memory use',
|
||||
label: 'Memory Use',
|
||||
icon: memoryIcon,
|
||||
width: 26,
|
||||
setting: 'mem',
|
||||
@@ -177,7 +189,7 @@ export default class Row extends React.Component<RowProps, {}> {
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<tr className={className} onClick={this.toggle}>
|
||||
{
|
||||
Row.columns
|
||||
.filter(({ setting }) => setting == null || settings[setting])
|
||||
@@ -186,4 +198,14 @@ export default class Row extends React.Component<RowProps, {}> {
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
public toggle = () => {
|
||||
const { pins, node } = this.props;
|
||||
|
||||
if (node.pinned) {
|
||||
pins.delete(node.id)
|
||||
} else {
|
||||
pins.add(node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { Icon } from './';
|
||||
|
||||
import './Option.css';
|
||||
|
||||
export namespace Option {
|
||||
export interface Props {
|
||||
icon: string;
|
||||
label: string;
|
||||
checked: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
export function Option(props: Option.Props): React.ReactElement<any> {
|
||||
const className = props.checked ? "Option Option-on" : "Option";
|
||||
|
||||
return (
|
||||
<p className={className} onClick={props.onClick}>
|
||||
<Icon src={props.icon} alt={props.label} />
|
||||
{props.label}
|
||||
<span className="Option-switch">
|
||||
<span className="Option-knob" />
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
+7
-7
@@ -1,19 +1,19 @@
|
||||
.Option {
|
||||
.Setting {
|
||||
color: #666;
|
||||
padding: 0;
|
||||
margin: 0 0 8px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Option-on {
|
||||
.Setting-on {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.Option .Icon {
|
||||
.Setting .Icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.Option-switch {
|
||||
.Setting-switch {
|
||||
width: 40px;
|
||||
height: 18px;
|
||||
border-radius: 18px;
|
||||
@@ -24,12 +24,12 @@
|
||||
transition: background-color 0.15s linear, border-color 0.15s linear;
|
||||
}
|
||||
|
||||
.Option-on .Option-switch {
|
||||
.Setting-on .Setting-switch {
|
||||
background: #d64ca8;
|
||||
border-color: #d64ca8;
|
||||
}
|
||||
|
||||
.Option-knob {
|
||||
.Setting-knob {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #fff;
|
||||
@@ -42,6 +42,6 @@
|
||||
transition: left 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.Option-on .Option-knob {
|
||||
.Setting-on .Setting-knob {
|
||||
left: 22px;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import * as React from 'react';
|
||||
import { Icon } from './';
|
||||
import { State } from '../state';
|
||||
import { PersistentObject } from '../persist';
|
||||
|
||||
import './Setting.css';
|
||||
|
||||
export namespace Setting {
|
||||
export interface Props {
|
||||
icon: string;
|
||||
label: string;
|
||||
setting: keyof State.Settings;
|
||||
settings: PersistentObject<State.Settings>;
|
||||
}
|
||||
}
|
||||
|
||||
export class Setting extends React.Component<Setting.Props, {}> {
|
||||
public render() {
|
||||
const { icon, label, setting, settings } = this.props;
|
||||
|
||||
const checked = settings.get(setting);
|
||||
const className = checked ? "Setting Setting-on" : "Setting";
|
||||
|
||||
return (
|
||||
<p className={className} onClick={this.toggle}>
|
||||
<Icon src={icon} alt={label} />
|
||||
{label}
|
||||
<span className="Setting-switch">
|
||||
<span className="Setting-knob" />
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
private toggle = () => {
|
||||
const { setting, settings } = this.props;
|
||||
|
||||
settings.set(setting, !settings.get(setting));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export * from './Icon';
|
||||
export * from './Tile';
|
||||
export * from './Ago';
|
||||
export * from './OfflineIndicator';
|
||||
export * from './Option';
|
||||
export * from './Setting';
|
||||
|
||||
import * as Node from './Node';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user