diff --git a/package.json b/package.json
index 1ab5b58..cd0933a 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"dependencies": {
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-polyfill": "^6.26.0",
+ "bencode": "^1.0.0",
"bootstrap": "^4.0.0-alpha.6",
"express": "^4.15.3",
"file-loader": "^0.11.2",
diff --git a/scss/main.scss b/scss/main.scss
index f7d2557..22f7f7f 100644
--- a/scss/main.scss
+++ b/scss/main.scss
@@ -49,6 +49,12 @@
background-position: left bottom;
}
+input[type="text"], input[type="number"] {
+ padding: 0.05rem 0.25rem;
+ border-radius: 0;
+ border-color: #888;
+}
+
.navbar {
input[type="text"] {
margin-top: 7px;
@@ -103,3 +109,28 @@
margin-bottom: 0;
}
}
+
+.card {
+ .card {
+ border-left: none;
+ border-right: none;
+ }
+
+ .form-group {
+ margin-bottom: 0.25rem;
+ }
+}
+
+select.form-control {
+ height: 1.5rem !important;
+ padding: 0;
+ font-size: 0.9rem;
+}
+
+.form-check-input:only-child {
+ position: absolute;
+}
+
+.form-check-inline {
+ margin-bottom: 0;
+}
diff --git a/src/bitrate.js b/src/bitrate.js
index 33e750d..b30d94d 100644
--- a/src/bitrate.js
+++ b/src/bitrate.js
@@ -1,10 +1,22 @@
+export const Rates = {
+ "b/s": Math.pow(1024, 0),
+ "KiB/s": Math.pow(1024, 1),
+ "MiB/s": Math.pow(1024, 2),
+ "GiB/s": Math.pow(1024, 3),
+ "TiB/s": Math.pow(1024, 4),
+};
+
+export function convertToBitrate(value, unit) {
+ return value * Rates[unit];
+}
+
export function formatBitrate(bitrate) {
- if (bitrate > 1000000000) {
- return `${(bitrate / 1000000000).toFixed(2)} Gb/s`;
- } else if (bitrate > 1000000) {
- return `${(bitrate / 1000000).toFixed(2)} Mb/s`;
- } else if (bitrate > 1000) {
- return `${(bitrate / 1000).toFixed(2)} Kb/s`;
+ if (bitrate > Rates["GiB/s"]) {
+ return `${(bitrate / Rates["GiB/s"]).toFixed(2)} GiB/s`;
+ } else if (bitrate > Rates["MiB/s"]) {
+ return `${(bitrate / Rates["MiB/s"]).toFixed(2)} MiB/s`;
+ } else if (bitrate > Rates["KiB/s"]) {
+ return `${(bitrate / Rates["KiB/s"]).toFixed(2)} KiB/s`;
} else {
return `${bitrate} b/s`;
}
diff --git a/src/ui/add_torrent.js b/src/ui/add_torrent.js
index 7dbb198..75b386d 100644
--- a/src/ui/add_torrent.js
+++ b/src/ui/add_torrent.js
@@ -2,10 +2,126 @@ import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
-import { Progress, Collapse, Card, CardBlock } from 'reactstrap';
-import FontAwesome from 'react-fontawesome';
+import {
+ Progress,
+ Card,
+ CardBlock,
+ CardTitle,
+ CardText,
+ FormGroup,
+ Label,
+ Input
+} from 'reactstrap';
+import ToggleContainer from './toggle_container';
import fetch from 'isomorphic-fetch';
+import bencode from 'bencode';
import ws_send from '../socket';
+import { convertToBitrate } from '../bitrate';
+
+class Throttle extends Component {
+ constructor() {
+ super();
+ this.state = {
+ strategy: "global",
+ unit: "MiB/s",
+ limit: 1
+ };
+ this.setStrategy = this.setStrategy.bind(this);
+ }
+
+ invokeChange() {
+ const { onChange } = this.props;
+ const { strategy, limit, unit } = this.state;
+ this.onChange && this.onChange(strategy, convertToBitrate(limit, unit));
+ }
+
+ setStrategy(strategy) {
+ this.setState({ strategy });
+ this.invokeChange();
+ }
+
+ setLimit(limit) {
+ if (limit <= 0) {
+ this.setState({ limit: this.state.limit });
+ return;
+ }
+ this.setState({ limit });
+ this.invokeChange();
+ }
+
+ setUnit(unit) {
+ this.setState({ unit });
+ this.invokeChange();
+ }
+
+ render() {
+ const { legend, prop } = this.props;
+ return (
+
+ );
+ }
+}
class AddTorrent extends Component {
constructor() {
@@ -13,7 +129,10 @@ class AddTorrent extends Component {
this.state = {
loading: false,
customize: false,
- file: null
+ file: null,
+ torrent: null,
+ files: [],
+ startImmediately: true
};
}
@@ -27,7 +146,7 @@ class AddTorrent extends Component {
headers: headers
});
} catch (ex) {
- // TODO: synapse borks up this response
+ // TODO: something more useful
console.log(ex);
}
}
@@ -36,32 +155,143 @@ class AddTorrent extends Component {
this.setState({ loading: true });
const { file } = this.state;
const { dispatch } = this.props;
- const reader = new FileReader();
- reader.onload = e => {
- ws_send("UPLOAD_TORRENT", { size: file.size }, async offer => {
- switch (offer.type) {
- case "TRANSFER_OFFER":
- return await this.handleTransferOffer(offer, file);
- case "RESOURCES_EXTANT":
- const [id] = offer.ids;
- dispatch(push(`/torrents/${id}`));
- break;
- }
+ ws_send("UPLOAD_TORRENT", { size: file.size }, async offer => {
+ switch (offer.type) {
+ case "TRANSFER_OFFER":
+ return await this.handleTransferOffer(offer, file);
+ case "RESOURCES_EXTANT":
+ const [id] = offer.ids;
+ dispatch(push(`/torrents/${id}`));
+ break;
+ }
+ });
+ }
+
+ processTorrent(torrent) {
+ const { info } = torrent;
+ if (!info.files) {
+ this.setState({
+ files: [
+ {
+ name: info.name,
+ length: info.length
+ }
+ ],
+ torrent
});
- };
- reader.readAsArrayBuffer(file);
+ } else {
+ // TODO
+ }
}
handleFile(e) {
const file = e.target.files[0];
+ const reader = new FileReader();
+ reader.onload = e => {
+ try {
+ const torrent = bencode.decode(reader.result, 'utf8');
+ this.processTorrent.bind(this)(torrent);
+ } catch (ex) {
+ // TODO: something meaningful
+ console.log(ex);
+ }
+ };
+ reader.readAsArrayBuffer(file);
this.setState({ file });
}
+ renderOptions() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ renderTorrent() {
+ const { torrent, file, files, loading } = this.state;
+
+ const details = {
+ "comment": d => d,
+ "creation date": d => new Date(d * 1000).toDateString(),
+ "created by": d => d
+ };
+
+ return (
+
+
+ {file.name}
+
+
+ {Object.keys(details).map(key =>
+ torrent[key] && (
+
+
- {key}
+ - {details[key](torrent[key])}
+
+ )
+ )}
+
+
+
+
+ {this.renderOptions.bind(this)()}
+
+
+
+
+ TODO
+
+
+
+
+ {loading ?
+ : null}
+
+ );
+ }
+
render() {
- const { file, loading } = this.state;
return (
Add torrent
+ {this.state.torrent && this.renderTorrent.bind(this)()}
-
-
{file ? file.name : ""}
-
-
-
-
-
-
-
-
- TODO
-
-
-
-
-
-
-
+ {this.state.torrent ?
+
+ className="btn btn-default"
+ onClick={() => findDOMNode(this).querySelector("input[type='file']").click()}
+ >Select a different torrent?
-
-
+ :
+
+ }
);
diff --git a/src/ui/toggle_container.js b/src/ui/toggle_container.js
new file mode 100644
index 0000000..6e46af7
--- /dev/null
+++ b/src/ui/toggle_container.js
@@ -0,0 +1,30 @@
+import React, { Component } from 'react';
+import FontAwesome from 'react-fontawesome';
+import { Collapse } from 'reactstrap';
+
+export default class ToggleContainer extends Component {
+ constructor() {
+ super();
+ this.state = { open: false };
+ }
+
+ render() {
+ return (
+
+
+
+ {this.props.children}
+
+
+ );
+ }
+}