index.community/frontend/src/components/Sidebar.tsx

183 lines
6.7 KiB
TypeScript
Raw Normal View History

2018-09-01 17:34:00 +00:00
import * as moment from 'moment';
2018-09-01 17:24:05 +00:00
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import * as sanitize from 'sanitize-html';
import { Card, Classes, Divider, Elevation, HTMLTable, NonIdealState } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { selectAndLoadInstance } from '../redux/actions';
import { IAppState, IInstanceDetails } from '../redux/types';
interface ISidebarProps {
instanceName: string | null,
instanceDetails: IInstanceDetails | null,
isLoadingInstanceDetails: boolean;
selectAndLoadInstance: (instanceName: string) => void;
}
class SidebarImpl extends React.Component<ISidebarProps> {
public render() {
return (
2018-09-03 14:10:44 +00:00
<Card className="fediverse-sidebar" elevation={Elevation.THREE}>
2018-09-01 17:24:05 +00:00
{this.renderSidebarContents()}
</Card>
)
}
private renderSidebarContents = () => {
if (this.props.isLoadingInstanceDetails) {
return this.renderLoadingState();
} else if (!this.props.instanceDetails) {
return this.renderEmptyState();
}
return (
<div>
<h2>{this.props.instanceName || "No instance selected"}</h2>
<Divider />
{this.renderDescription()}
{this.renderVersion()}
{this.renderCounts()}
{this.renderPeers()}
</div>
);
}
private renderDescription = () => {
const description = this.props.instanceDetails!.description;
if (!description) {
return;
}
return (
<div>
<h4>Description</h4>
<div className={Classes.RUNNING_TEXT} dangerouslySetInnerHTML={{__html: sanitize(description)}} />
<Divider />
</div>
)
}
private renderVersion = () => {
const version = this.props.instanceDetails!.version;
if (!version) {
return;
}
return (
<div>
<h4>Version</h4>
<code className={Classes.CODE}>{version}</code>
<Divider />
</div>
)
}
private renderCounts = () => {
const userCount = this.props.instanceDetails!.userCount;
const statusCount = this.props.instanceDetails!.statusCount;
const domainCount = this.props.instanceDetails!.domainCount;
2018-09-01 17:34:00 +00:00
const lastUpdated = this.props.instanceDetails!.lastUpdated;
2018-09-01 17:24:05 +00:00
if (!userCount && !statusCount && !domainCount) {
return;
}
return (
<div>
<h4>Stats</h4>
<HTMLTable small={true} striped={true} className="fediverse-sidebar-table">
<tbody>
<tr>
<td>Users</td>
<td>{userCount || "Unknown"}</td>
</tr>
<tr>
<td>Statuses</td>
<td>{statusCount || "Unknown"}</td>
</tr>
<tr>
<td>Known peers</td>
<td>{domainCount || "Unknown"}</td>
</tr>
2018-09-01 17:34:00 +00:00
<tr>
<td>Last updated</td>
<td>{moment(lastUpdated + "Z").fromNow() || "Unknown"}</td>
</tr>
2018-09-01 17:24:05 +00:00
</tbody>
</HTMLTable>
<Divider />
</div>
)
}
private renderPeers = () => {
const peers = this.props.instanceDetails!.peers;
2018-09-01 17:34:00 +00:00
if (!peers || peers.length === 0) {
2018-09-01 17:24:05 +00:00
return;
}
const peerRows = peers.map(instance => (
<tr key={instance.name} onClick={this.selectInstance}>
<td>{instance.name}</td>
</tr>
));
return (
<div>
<h4>Known instances</h4>
<HTMLTable small={true} striped={true} interactive={true} className="fediverse-sidebar-table">
<tbody>
{peerRows}
</tbody>
</HTMLTable>
</div>
)
}
private renderEmptyState = () => {
return (
<NonIdealState
icon={IconNames.CIRCLE}
title="No instance selected"
description="Select an instance from the graph or the top-right dropdown to see its details."
/>
)
}
private renderLoadingState = () => {
return (
<div>
<h4><span className={Classes.SKELETON}>Description</span></h4>
<p className={Classes.SKELETON}>
Eaque rerum sequi unde omnis voluptatibus non quia fugit. Dignissimos asperiores aut incidunt.
Cupiditate sit voluptates quia nulla et saepe id suscipit.
Voluptas sed rerum placeat consectetur pariatur necessitatibus tempora.
Eaque rerum sequi unde omnis voluptatibus non quia fugit. Dignissimos asperiores aut incidunt.
Cupiditate sit voluptates quia nulla et saepe id suscipit.
Voluptas sed rerum placeat consectetur pariatur necessitatibus tempora.
</p>
<h4><span className={Classes.SKELETON}>Version</span></h4>
<p className={Classes.SKELETON}>
Eaque rerum sequi unde omnis voluptatibus non quia fugit.
</p>
<h4><span className={Classes.SKELETON}>Stats</span></h4>
<p className={Classes.SKELETON}>
Eaque rerum sequi unde omnis voluptatibus non quia fugit. Dignissimos asperiores aut incidunt.
Cupiditate sit voluptates quia nulla et saepe id suscipit.
Eaque rerum sequi unde omnis voluptatibus non quia fugit. Dignissimos asperiores aut incidunt.
Cupiditate sit voluptates quia nulla et saepe id suscipit.
</p>
</div>
);
}
private selectInstance = (e: any)=> {
this.props.selectAndLoadInstance(e.target.innerText);
}
}
const mapStateToProps = (state: IAppState) => ({
instanceDetails: state.currentInstance.currentInstanceDetails,
instanceName: state.currentInstance.currentInstanceName,
isLoadingInstanceDetails: state.currentInstance.isLoadingInstanceDetails,
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
selectAndLoadInstance: (instanceName: string) => dispatch(selectAndLoadInstance(instanceName) as any),
});
export const Sidebar = connect(mapStateToProps, mapDispatchToProps)(SidebarImpl);