mirror of
https://github.com/zoriya/flood.git
synced 2025-12-20 14:15:15 +00:00
Torrent details overhaul
This commit is contained in:
@@ -55,10 +55,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__tree {
|
&__tree {
|
||||||
padding-left: 1px;
|
padding-left: 3px;
|
||||||
|
|
||||||
.directory-tree__tree {
|
.directory-tree__tree {
|
||||||
padding-left: 3px;
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,12 @@
|
|||||||
background: $torrent-details--background;
|
background: $torrent-details--background;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
box-shadow: -1px 0 0 $torrent-details--border;
|
box-shadow: -1px 0 0 $torrent-details--border;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
left: 0;
|
left: 0;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: $spacing-unit;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -24,6 +25,8 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
|
box-shadow: 0 1px 0 rgba(#040d13, 0.3);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
.torrent {
|
.torrent {
|
||||||
|
|
||||||
@@ -66,10 +69,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section {
|
|
||||||
margin-bottom: $spacing-unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__table {
|
&__table {
|
||||||
color: $torrent-details--table--foreground;
|
color: $torrent-details--table--foreground;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -92,7 +91,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__heading,
|
||||||
|
&__content,
|
||||||
|
&__navigation {
|
||||||
|
padding: $spacing-unit * 1/2 $spacing-unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__navigation {
|
||||||
|
box-shadow: 1px 0 0 rgba(#040d13, 0.5);
|
||||||
|
background: rgba(#0c1b26, 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent-details-enter {
|
.torrent-details-enter {
|
||||||
|
|||||||
@@ -4,26 +4,22 @@ import React from 'react';
|
|||||||
import CSSTransitionGroup from 'react-addons-css-transition-group';
|
import CSSTransitionGroup from 'react-addons-css-transition-group';
|
||||||
|
|
||||||
import ApplicationPanel from '../layout/ApplicationPanel';
|
import ApplicationPanel from '../layout/ApplicationPanel';
|
||||||
import Download from '../icons/Download';
|
|
||||||
import EventTypes from '../../constants/EventTypes';
|
import EventTypes from '../../constants/EventTypes';
|
||||||
import ETA from '../icons/ETA';
|
|
||||||
import format from '../../util/formatData';
|
import format from '../../util/formatData';
|
||||||
import ProgressBar from '../ui/ProgressBar';
|
import NavigationList from '../ui/NavigationList';
|
||||||
import Ratio from '../../components/icons/Ratio';
|
|
||||||
import TorrentActions from '../../actions/TorrentActions';
|
import TorrentActions from '../../actions/TorrentActions';
|
||||||
import TorrentFiles from '../torrent-details/TorrentFiles';
|
import TorrentFiles from '../torrent-details/TorrentFiles';
|
||||||
|
import TorrentHeading from '../torrent-details/TorrentHeading';
|
||||||
import TorrentPeers from '../torrent-details/TorrentPeers';
|
import TorrentPeers from '../torrent-details/TorrentPeers';
|
||||||
import TorrentStore from '../../stores/TorrentStore';
|
import TorrentStore from '../../stores/TorrentStore';
|
||||||
import TorrentTrackers from '../torrent-details/TorrentTrackers';
|
import TorrentTrackers from '../torrent-details/TorrentTrackers';
|
||||||
import UIStore from '../../stores/UIStore';
|
import UIStore from '../../stores/UIStore';
|
||||||
import Upload from '../icons/Upload';
|
|
||||||
|
|
||||||
const METHODS_TO_BIND = [
|
const METHODS_TO_BIND = [
|
||||||
|
'handleNavChange',
|
||||||
'onTorrentDetailsHashChange',
|
'onTorrentDetailsHashChange',
|
||||||
'onOpenChange',
|
'onOpenChange',
|
||||||
'onTorrentDetailsChange',
|
'onTorrentDetailsChange'
|
||||||
'getHeading',
|
|
||||||
'getTorrentDetailsView'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class TorrentDetails extends React.Component {
|
export default class TorrentDetails extends React.Component {
|
||||||
@@ -36,7 +32,8 @@ export default class TorrentDetails extends React.Component {
|
|||||||
torrentDetailsError: false,
|
torrentDetailsError: false,
|
||||||
selectedTorrent: {},
|
selectedTorrent: {},
|
||||||
selectedTorrentHash: null,
|
selectedTorrentHash: null,
|
||||||
torrentDetails: {}
|
torrentDetails: {},
|
||||||
|
torrentDetailsPane: 'files'
|
||||||
};
|
};
|
||||||
|
|
||||||
METHODS_TO_BIND.forEach((method) => {
|
METHODS_TO_BIND.forEach((method) => {
|
||||||
@@ -81,111 +78,76 @@ export default class TorrentDetails extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeading(torrent) {
|
getNavigationItem(item) {
|
||||||
let added = new Date(torrent.added * 1000);
|
let selectedHash = UIStore.getTorrentDetailsHash();
|
||||||
let addedString = `${added.getMonth() + 1}/${added.getDate()}/` +
|
let torrent = TorrentStore.getTorrent(selectedHash);
|
||||||
`${added.getFullYear()}`;
|
let torrentDetails = this.state.torrentDetails || {};
|
||||||
let completed = format.data(torrent.bytesDone);
|
|
||||||
let downloadRate = format.data(torrent.downloadRate, '/s');
|
|
||||||
let downloadTotal = format.data(torrent.downloadTotal);
|
|
||||||
let eta = format.eta(torrent.eta);
|
|
||||||
let ratio = format.ratio(torrent.ratio);
|
|
||||||
let totalSize = format.data(torrent.sizeBytes);
|
|
||||||
let uploadRate = format.data(torrent.uploadRate, '/s');
|
|
||||||
let uploadTotal = format.data(torrent.uploadTotal);
|
|
||||||
|
|
||||||
let classes = classNames('torrent-details__heading torrent-details__section', {
|
switch (item) {
|
||||||
'is-selected': this.props.selected,
|
case 'files':
|
||||||
'is-stopped': torrent.status.indexOf('is-stopped') > -1,
|
return <TorrentFiles files={torrentDetails.files} torrent={torrent} />;
|
||||||
'is-paused': torrent.status.indexOf('is-paused') > -1,
|
break;
|
||||||
'is-actively-downloading': downloadRate.value > 0,
|
case 'trackers':
|
||||||
'is-downloading': torrent.status.indexOf('is-downloading') > -1,
|
return <TorrentTrackers trackers={torrentDetails.trackers} />;
|
||||||
'is-seeding': torrent.status.indexOf('is-seeding') > -1,
|
break;
|
||||||
'is-completed': torrent.status.indexOf('is-completed') > -1,
|
case 'peers':
|
||||||
'is-checking': torrent.status.indexOf('is-checking') > -1,
|
return <TorrentPeers peers={torrentDetails.peers} />;
|
||||||
'is-active': torrent.status.indexOf('is-active') > -1,
|
break;
|
||||||
'is-inactive': torrent.status.indexOf('is-inactive') > -1
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return null;
|
||||||
<div className={classes}>
|
|
||||||
<h1 className="torrent__details--name">{torrent.name}</h1>
|
|
||||||
<ul className="torrent__details torrent__details--tertiary">
|
|
||||||
<li className="torrent__details--download transfer-data--download">
|
|
||||||
<Download />
|
|
||||||
{downloadRate.value}
|
|
||||||
<em className="unit">{downloadRate.unit}</em>
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--upload transfer-data--upload">
|
|
||||||
<Upload />
|
|
||||||
{uploadRate.value}
|
|
||||||
<em className="unit">{uploadRate.unit}</em>
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--ratio">
|
|
||||||
<Ratio />
|
|
||||||
{ratio}
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--eta">
|
|
||||||
<ETA />
|
|
||||||
{eta}
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--completed">
|
|
||||||
<span className="torrent__details__label">Downloaded</span>
|
|
||||||
{torrent.percentComplete}
|
|
||||||
<em className="unit">%</em>
|
|
||||||
—
|
|
||||||
{completed.value}
|
|
||||||
<em className="unit">{completed.unit}</em>
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--uploaded">
|
|
||||||
<span className="torrent__details__label">Uploaded</span>
|
|
||||||
{uploadTotal.value}
|
|
||||||
<em className="unit">{uploadTotal.unit}</em>
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--size">
|
|
||||||
<span className="torrent__details__label">Size</span>
|
|
||||||
{totalSize.value}
|
|
||||||
<em className="unit">{totalSize.unit}</em>
|
|
||||||
</li>
|
|
||||||
<li className="torrent__details--added">
|
|
||||||
<span className="torrent__details__label">Added</span>
|
|
||||||
{addedString}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ProgressBar percent={torrent.percentComplete} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTorrentDetailsView() {
|
getNavigationItems() {
|
||||||
let torrentDetailsContent = null;
|
return [
|
||||||
|
{
|
||||||
|
slug: 'files',
|
||||||
|
label: 'Files'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'peers',
|
||||||
|
label: 'Peers'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'trackers',
|
||||||
|
label: 'Trackers'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavChange(item) {
|
||||||
|
this.setState({torrentDetailsPane: item.slug});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let detailContent = null;
|
||||||
|
|
||||||
if (this.state.isOpen) {
|
if (this.state.isOpen) {
|
||||||
let selectedHash = UIStore.getTorrentDetailsHash();
|
let selectedHash = UIStore.getTorrentDetailsHash();
|
||||||
let torrent = TorrentStore.getTorrent(selectedHash);
|
let torrent = TorrentStore.getTorrent(selectedHash);
|
||||||
let torrentDetails = this.state.torrentDetails || {};
|
|
||||||
|
|
||||||
torrentDetailsContent = (
|
detailContent = (
|
||||||
<div className="torrent-details">
|
<div className="torrent-details">
|
||||||
{this.getHeading(torrent)}
|
<TorrentHeading torrent={torrent} />
|
||||||
<TorrentTrackers trackers={torrentDetails.trackers} />
|
<div className="torrent-details__content__wrapper">
|
||||||
<TorrentFiles files={torrentDetails.files} torrent={torrent} />
|
<NavigationList defaultItem={this.state.torrentDetailsPane}
|
||||||
<TorrentPeers peers={torrentDetails.peers} />
|
items={this.getNavigationItems()} onChange={this.handleNavChange}
|
||||||
|
uniqueClassName="torrent-details__navigation" />
|
||||||
|
<div className="torrent-details__content">
|
||||||
|
{this.getNavigationItem(this.state.torrentDetailsPane)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return torrentDetailsContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<ApplicationPanel modifier="torrent-details">
|
<ApplicationPanel modifier="torrent-details">
|
||||||
<CSSTransitionGroup
|
<CSSTransitionGroup
|
||||||
transitionEnterTimeout={1000}
|
transitionEnterTimeout={1000}
|
||||||
transitionLeaveTimeout={1000}
|
transitionLeaveTimeout={1000}
|
||||||
transitionName="torrent-details">
|
transitionName="torrent-details">
|
||||||
{this.getTorrentDetailsView()}
|
{detailContent}
|
||||||
</CSSTransitionGroup>
|
</CSSTransitionGroup>
|
||||||
</ApplicationPanel>
|
</ApplicationPanel>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class NavigationList extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
constructor();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selectedItem: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getNavigationItems(items) {
|
||||||
|
return items.map((item, index) => {
|
||||||
|
let classes = classnames(this.props.itemClassName, {
|
||||||
|
this.props.selectedClassName: item.slug === this.state.selectedItem
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className={classes} key={index}
|
||||||
|
onClick={this.handleItemClick.bind(this, item)}>
|
||||||
|
{item.label}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemClick(item) {
|
||||||
|
this.setState({
|
||||||
|
selectedItem: item.slug
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.onChange(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ul className={this.props.listClassName}>
|
||||||
|
{this.getNavigationItems(this.props.items)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationList.defaultProps = {
|
||||||
|
itemClassName: 'navigation__item',
|
||||||
|
listClassName: 'navigation'
|
||||||
|
};
|
||||||
|
|
||||||
|
NavigationList.propTypes = {
|
||||||
|
defaultItem: React.PropTypes.string,
|
||||||
|
itemClassName: React.PropTypes.string,
|
||||||
|
items: React.PropTypes.array,
|
||||||
|
listClassName: React.PropTypes.string,
|
||||||
|
onChange: React.PropTypes.func,
|
||||||
|
selectedClassName: React.PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavigationList;
|
||||||
@@ -48,10 +48,27 @@ export default class TorrentFiles extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFileData(torrent, files) {
|
getFileData(torrent, files) {
|
||||||
let parentDirectory = torrent.directory;
|
|
||||||
let filename = torrent.filename;
|
|
||||||
|
|
||||||
if (files) {
|
}
|
||||||
|
|
||||||
|
handleParentDirectoryClick() {
|
||||||
|
this.setState({
|
||||||
|
expanded: !this.state.expanded
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoaded() {
|
||||||
|
if (this.props.files) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {files, torrent} = this.props;
|
||||||
|
|
||||||
|
if (this.isLoaded()) {
|
||||||
// We've received full file details from the client.
|
// We've received full file details from the client.
|
||||||
let fileList = null;
|
let fileList = null;
|
||||||
|
|
||||||
@@ -64,7 +81,7 @@ export default class TorrentFiles extends React.Component {
|
|||||||
<div className="directory-tree__node directory-tree__parent-directory"
|
<div className="directory-tree__node directory-tree__parent-directory"
|
||||||
onClick={this.handleParentDirectoryClick}>
|
onClick={this.handleParentDirectoryClick}>
|
||||||
<FolderOpenSolid />
|
<FolderOpenSolid />
|
||||||
{parentDirectory}
|
{torrent.directory}
|
||||||
</div>
|
</div>
|
||||||
{fileList}
|
{fileList}
|
||||||
</div>
|
</div>
|
||||||
@@ -73,26 +90,17 @@ export default class TorrentFiles extends React.Component {
|
|||||||
// We've only received the top-level file details from the torrent list.
|
// We've only received the top-level file details from the torrent list.
|
||||||
return (
|
return (
|
||||||
<div className="directory-tree torrent-details__section">
|
<div className="directory-tree torrent-details__section">
|
||||||
<div className="directory-tree__node directory-tree__parent-directory">
|
<div className="directory-tree__node
|
||||||
|
directory-tree__parent-directory">
|
||||||
<FolderOpenSolid />
|
<FolderOpenSolid />
|
||||||
{parentDirectory}
|
{torrent.directory}
|
||||||
</div>
|
</div>
|
||||||
<div className="directory-tree__node directory-tree__node--file">
|
<div className="directory-tree__node directory-tree__node--file">
|
||||||
<File />
|
<File />
|
||||||
{filename}
|
{torrent.filename}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleParentDirectoryClick() {
|
|
||||||
this.setState({
|
|
||||||
expanded: !this.state.expanded
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.getFileData(this.props.torrent, this.props.files);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import FolderOpenSolid from '../icons/FolderOpenSolid';
|
||||||
|
import DirectoryTree from '../filesystem/DirectoryTree';
|
||||||
|
import Download from '../icons/Download';
|
||||||
|
import ETA from '../icons/ETA';
|
||||||
|
import File from '../icons/File';
|
||||||
|
import format from '../../util/formatData';
|
||||||
|
import ProgressBar from '../ui/ProgressBar';
|
||||||
|
import Ratio from '../../components/icons/Ratio';
|
||||||
|
import Upload from '../icons/Upload';
|
||||||
|
|
||||||
|
export default class TorrentHeading extends React.Component {
|
||||||
|
render() {
|
||||||
|
let torrent = this.props.torrent;
|
||||||
|
let added = new Date(torrent.added * 1000);
|
||||||
|
let addedString = `${added.getMonth() + 1}/${added.getDate()}/` +
|
||||||
|
`${added.getFullYear()}`;
|
||||||
|
let completed = format.data(torrent.bytesDone);
|
||||||
|
let downloadRate = format.data(torrent.downloadRate, '/s');
|
||||||
|
let downloadTotal = format.data(torrent.downloadTotal);
|
||||||
|
let eta = format.eta(torrent.eta);
|
||||||
|
let ratio = format.ratio(torrent.ratio);
|
||||||
|
let totalSize = format.data(torrent.sizeBytes);
|
||||||
|
let uploadRate = format.data(torrent.uploadRate, '/s');
|
||||||
|
let uploadTotal = format.data(torrent.uploadTotal);
|
||||||
|
|
||||||
|
let classes = classNames('torrent-details__heading', {
|
||||||
|
'is-selected': this.props.selected,
|
||||||
|
'is-stopped': torrent.status.indexOf('is-stopped') > -1,
|
||||||
|
'is-paused': torrent.status.indexOf('is-paused') > -1,
|
||||||
|
'is-actively-downloading': downloadRate.value > 0,
|
||||||
|
'is-downloading': torrent.status.indexOf('is-downloading') > -1,
|
||||||
|
'is-seeding': torrent.status.indexOf('is-seeding') > -1,
|
||||||
|
'is-completed': torrent.status.indexOf('is-completed') > -1,
|
||||||
|
'is-checking': torrent.status.indexOf('is-checking') > -1,
|
||||||
|
'is-active': torrent.status.indexOf('is-active') > -1,
|
||||||
|
'is-inactive': torrent.status.indexOf('is-inactive') > -1
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes}>
|
||||||
|
<h1 className="torrent__details--name">{torrent.name}</h1>
|
||||||
|
<ul className="torrent__details torrent__details--tertiary">
|
||||||
|
<li className="torrent__details--download transfer-data--download">
|
||||||
|
<Download />
|
||||||
|
{downloadRate.value}
|
||||||
|
<em className="unit">{downloadRate.unit}</em>
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--upload transfer-data--upload">
|
||||||
|
<Upload />
|
||||||
|
{uploadRate.value}
|
||||||
|
<em className="unit">{uploadRate.unit}</em>
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--ratio">
|
||||||
|
<Ratio />
|
||||||
|
{ratio}
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--eta">
|
||||||
|
<ETA />
|
||||||
|
{eta}
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--completed">
|
||||||
|
<span className="torrent__details__label">Downloaded</span>
|
||||||
|
{torrent.percentComplete}
|
||||||
|
<em className="unit">%</em>
|
||||||
|
—
|
||||||
|
{completed.value}
|
||||||
|
<em className="unit">{completed.unit}</em>
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--uploaded">
|
||||||
|
<span className="torrent__details__label">Uploaded</span>
|
||||||
|
{uploadTotal.value}
|
||||||
|
<em className="unit">{uploadTotal.unit}</em>
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--size">
|
||||||
|
<span className="torrent__details__label">Size</span>
|
||||||
|
{totalSize.value}
|
||||||
|
<em className="unit">{totalSize.unit}</em>
|
||||||
|
</li>
|
||||||
|
<li className="torrent__details--added">
|
||||||
|
<span className="torrent__details__label">Added</span>
|
||||||
|
{addedString}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ProgressBar percent={torrent.percentComplete} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,9 @@ import File from '../icons/File';
|
|||||||
import format from '../../util/formatData';
|
import format from '../../util/formatData';
|
||||||
|
|
||||||
export default class TorrentPeers extends React.Component {
|
export default class TorrentPeers extends React.Component {
|
||||||
getPeerList(peers) {
|
render() {
|
||||||
|
let peers = this.props.peers;
|
||||||
|
|
||||||
if (peers) {
|
if (peers) {
|
||||||
let peerList = null;
|
let peerList = null;
|
||||||
let peerCount = 0;
|
let peerCount = 0;
|
||||||
@@ -55,8 +57,4 @@ export default class TorrentPeers extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.getPeerList(this.props.peers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default class TorrentTrackrs extends React.Component {
|
export default class TorrentTrackrs extends React.Component {
|
||||||
getTrackerList(trackers = []) {
|
render() {
|
||||||
|
let trackers = this.props.trackers || [];
|
||||||
|
|
||||||
let trackerCount = trackers.length;
|
let trackerCount = trackers.length;
|
||||||
let trackerTypes = ['http', 'udp', 'dht'];
|
let trackerTypes = ['http', 'udp', 'dht'];
|
||||||
|
|
||||||
@@ -39,8 +41,4 @@ export default class TorrentTrackrs extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.getTrackerList(this.props.trackers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
73
client/source/scripts/components/ui/NavigationList.js
Normal file
73
client/source/scripts/components/ui/NavigationList.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class NavigationList extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selectedItem: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getNavigationItems(items) {
|
||||||
|
return items.map((item, index) => {
|
||||||
|
let selectedSlug = null;
|
||||||
|
|
||||||
|
if (this.state.selectedItem) {
|
||||||
|
selectedSlug = this.state.selectedItem;
|
||||||
|
} else {
|
||||||
|
selectedSlug = this.props.defaultItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
let classes = classnames(this.props.itemClassName, {
|
||||||
|
[this.props.selectedClassName]: item.slug === selectedSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className={classes} key={index}
|
||||||
|
onClick={this.handleItemClick.bind(this, item)}>
|
||||||
|
{item.label}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemClick(item) {
|
||||||
|
this.setState({
|
||||||
|
selectedItem: item.slug
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.onChange(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let classes = classnames(this.props.listClassName, {
|
||||||
|
[this.props.uniqueClassName]: this.props.uniqueClassName
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className={classes}>
|
||||||
|
{this.getNavigationItems(this.props.items)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationList.defaultProps = {
|
||||||
|
itemClassName: 'navigation__item',
|
||||||
|
listClassName: 'navigation',
|
||||||
|
selectedClassName: 'is-selected'
|
||||||
|
};
|
||||||
|
|
||||||
|
NavigationList.propTypes = {
|
||||||
|
defaultItem: React.PropTypes.string,
|
||||||
|
itemClassName: React.PropTypes.string,
|
||||||
|
items: React.PropTypes.array,
|
||||||
|
listClassName: React.PropTypes.string,
|
||||||
|
onChange: React.PropTypes.func,
|
||||||
|
selectedClassName: React.PropTypes.string,
|
||||||
|
uniqueClassName: React.PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavigationList;
|
||||||
File diff suppressed because one or more lines are too long
@@ -915,9 +915,9 @@ body {
|
|||||||
opacity: 0.75; }
|
opacity: 0.75; }
|
||||||
|
|
||||||
.directory-tree__tree {
|
.directory-tree__tree {
|
||||||
padding-left: 1px; }
|
padding-left: 3px; }
|
||||||
.directory-tree__tree .directory-tree__tree {
|
.directory-tree__tree .directory-tree__tree {
|
||||||
padding-left: 3px; }
|
padding-left: 6px; }
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -1288,34 +1288,46 @@ body {
|
|||||||
background: #162835;
|
background: #162835;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
box-shadow: -1px 0 0 rgba(26, 47, 61, 0.1);
|
box-shadow: -1px 0 0 rgba(26, 47, 61, 0.1);
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
left: 0;
|
left: 0;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 25px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
-webkit-transition: opacity 1s;
|
-webkit-transition: opacity 1s;
|
||||||
transition: opacity 1s;
|
transition: opacity 1s;
|
||||||
z-index: 2; }
|
z-index: 2; }
|
||||||
.torrent-details__heading .torrent__details--name {
|
.torrent-details__heading {
|
||||||
color: #97bbd5;
|
box-shadow: 0 1px 0 rgba(4, 13, 19, 0.3);
|
||||||
font-size: 1.7em;
|
-webkit-box-flex: 0;
|
||||||
font-weight: 300; }
|
-webkit-flex: 0 0 auto;
|
||||||
.torrent-details__heading .torrent__details--tertiary {
|
-ms-flex: 0 0 auto;
|
||||||
color: #7b9cb4;
|
flex: 0 0 auto; }
|
||||||
font-size: 1em; }
|
.torrent-details__heading .torrent__details--name {
|
||||||
.torrent-details__heading .torrent__details--download .icon, .torrent-details__heading .torrent__details--upload .icon, .torrent-details__heading .torrent__details--eta .icon, .torrent-details__heading .torrent__details--ratio .icon {
|
color: #97bbd5;
|
||||||
height: 12px;
|
font-size: 1.7em;
|
||||||
width: 12px; }
|
font-weight: 300; }
|
||||||
.torrent-details__heading .torrent__details--eta {
|
.torrent-details__heading .torrent__details--tertiary {
|
||||||
opacity: 1;
|
color: #7b9cb4;
|
||||||
visibility: visible; }
|
font-size: 1em; }
|
||||||
.torrent-details__heading .progress-bar:after {
|
.torrent-details__heading .torrent__details--download .icon, .torrent-details__heading .torrent__details--upload .icon, .torrent-details__heading .torrent__details--eta .icon, .torrent-details__heading .torrent__details--ratio .icon {
|
||||||
background: #2a3e4c; }
|
height: 12px;
|
||||||
.torrent-details__section {
|
width: 12px; }
|
||||||
margin-bottom: 25px; }
|
.torrent-details__heading .torrent__details--eta {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible; }
|
||||||
|
.torrent-details__heading .progress-bar:after {
|
||||||
|
background: #2a3e4c; }
|
||||||
.torrent-details__table {
|
.torrent-details__table {
|
||||||
color: #567285;
|
color: #567285;
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
@@ -1330,6 +1342,27 @@ body {
|
|||||||
color: #778c9e;
|
color: #778c9e;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 5px; }
|
margin-left: 5px; }
|
||||||
|
.torrent-details__heading, .torrent-details__content, .torrent-details__navigation {
|
||||||
|
padding: 12.5px 25px; }
|
||||||
|
.torrent-details__content {
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-webkit-flex: 1;
|
||||||
|
-ms-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto; }
|
||||||
|
.torrent-details__content__wrapper {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-webkit-flex: 1;
|
||||||
|
-ms-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden; }
|
||||||
|
.torrent-details__navigation {
|
||||||
|
box-shadow: 1px 0 0 rgba(4, 13, 19, 0.5);
|
||||||
|
background: rgba(12, 27, 38, 0.5); }
|
||||||
|
|
||||||
.torrent-details-enter {
|
.torrent-details-enter {
|
||||||
opacity: 0; }
|
opacity: 0; }
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user