diff --git a/src/reducers/resource.js b/src/reducers/resource.js
index d7f0125..f6db220 100644
--- a/src/reducers/resource.js
+++ b/src/reducers/resource.js
@@ -12,21 +12,17 @@ function hack(old, _new) {
export default function resourceReducer(type) {
return (state = {}, action) => {
+ let ns = {...state};
switch (action.type) {
case UPDATE_RESOURCES:
- return {
- ...state,
- ...action.resources
- .filter(r => r.type === type)
- .reduce((s, r) => ({
- ...s,
- [r.id]: hack(state[r.id], { ...state[r.id], ...r })
- }), {})
- };
+ action.resources
+ .filter(r => r.type ===type)
+ .map(r => ns[r.id] = hack(state[r.id], { ...state[r.id], ...r }));
+ return ns;
case RESOURCES_REMOVED:
- return Object.values(state)
- .filter(r => action.ids.indexOf(r.id) === -1)
- .reduce((s, r) => ({ ...s, [r.id]: r }), {});
+ action.ids
+ .map(id => delete ns[id]);
+ return ns;
case SOCKET_UPDATE:
const _state = action.state;
return _state === SOCKET_STATE.CONNECTING ? {} : state;
diff --git a/src/reducers/subscribe.js b/src/reducers/subscribe.js
index 97fc85f..f205a60 100644
--- a/src/reducers/subscribe.js
+++ b/src/reducers/subscribe.js
@@ -8,12 +8,12 @@ export default function subscribe(state = [], action) {
return [ ...state, ...ids.map(id => ({ serial, id })) ];
}
case UNSUBSCRIBE: {
- const { ids } = action;
- return state.filter(sub => ids.indexOf(sub.id) === -1);
+ const ids = new Set(action.ids);
+ return state.filter(sub => !ids.has(sub.id));
}
case RESOURCES_REMOVED: {
- const { ids } = action;
- return state.filter(sub => ids.indexOf(sub.id) === -1);
+ const ids = new Set(action.ids);
+ return state.filter(sub => !ids.has(sub.id));
}
}
return state;
diff --git a/src/ui/torrent_details.js b/src/ui/torrent_details.js
index da2b2c4..f7da266 100644
--- a/src/ui/torrent_details.js
+++ b/src/ui/torrent_details.js
@@ -35,55 +35,69 @@ function basename(path) {
return parts[parts.length - 1];
}
-function File({ dispatch, file }) {
- const { uri } = store.getState().socket;
- const { download_token } = store.getState().server;
- return (
-
-
-
-
-
dispatch(updateResource({
- id: file.id,
- priority: parseInt(e.target.value)
- }))}
+class File extends Component {
+ shouldComponentUpdate(nextProps, _) {
+ return nextProps.file !== this.props.file;
+ }
+
+ render() {
+ const { dispatch, file } = this.props;
+ const { uri } = store.getState().socket;
+ const { download_token } = store.getState().server;
+ return (
+
+
+
+
+ dispatch(updateResource({
+ id: file.id,
+ priority: parseInt(e.target.value)
+ }))}
+ >
+
+
+
+
+
+
+
+
-
- );
+ );
+ }
}
-function Peer({ peer }) {
- return (
-
-
{peer.ip}
-
{formatBitrate(peer.rate_up)} up
-
{formatBitrate(peer.rate_down)} down
-
has {`${(peer.availability * 100).toFixed(0)}%`}
-
- );
+class Peer extends Component {
+ shouldComponentUpdate(nextProps, _) {
+ return nextProps.peer !== this.props.peer;
+ }
+
+ render() {
+ const { peer } = this.props;
+ return (
+
+
{peer.ip}
+
{formatBitrate(peer.rate_up)} up
+
{formatBitrate(peer.rate_down)} down
+
has {`${(peer.availability * 100).toFixed(0)}%`}
+
+ );
+ }
}
// TODO: move to separate component
@@ -114,6 +128,14 @@ class Torrent extends Component {
};
}
+ shouldComponentUpdate(nextProps, nextState) {
+ return nextProps.torrent !== this.props.torrent
+ || nextState.peersShown !== this.state.peersShown
+ || nextState.trackersShown !== this.state.trackersShown
+ || nextState.filesShown !== this.state.filesShown
+ || nextState.infoShown !== this.state.infoShown;
+ }
+
toggleTorrentState(torrent) {
if (torrent.status === "paused") {
ws_send("RESUME_TORRENT", { id: torrent.id });
@@ -222,9 +244,11 @@ class Torrent extends Component {
- {files.slice().sort((a, b) =>
- a.path.localeCompare(b.path)).map(file =>
- )}
+ {this.state.filesShown
+ ? files.slice()
+ .sort((a, b) => a.path.localeCompare(b.path))
+ .map(file => )
+ : null}
@@ -264,7 +288,7 @@ class Torrent extends Component {
- {peers.map(peer =>
)}
+ {this.state.peersShown ? peers.map(peer =>
) : null}
{peers.length === 0 &&
@@ -361,15 +385,21 @@ class TorrentDetails extends Component {
selection,
dispatch
} = this.props;
- const _files = Object.values(files).reduce((s, f) => ({
- ...s, [f.torrent_id]: [...(s[f.torrent_id] || []), f]
- }), {});
- const _trackers = Object.values(trackers).reduce((s, t) => ({
- ...s, [t.torrent_id]: [...(s[t.torrent_id] || []), t]
- }), {});
- const _peers = Object.values(peers).reduce((s, p) => ({
- ...s, [p.torrent_id]: [...(s[p.torrent_id] || []), p]
- }), {});
+ const index_by_tid = (res) => {
+ let _indexed = {};
+ Object.values(res).map(r => {
+ if (!(r.torrent_id in _indexed)) {
+ _indexed[r.torrent_id] = [];
+ }
+ _indexed[r.torrent_id].push(r);
+ });
+ return _indexed;
+ };
+
+ const _files = index_by_tid(files);
+ const _trackers = index_by_tid(trackers);
+ const _peers = index_by_tid(peers);
+
return (
{selection.length > 1 ? this.renderHeader.bind(this)() : null}
diff --git a/src/ui/torrent_table.js b/src/ui/torrent_table.js
index c80d5de..d68665c 100644
--- a/src/ui/torrent_table.js
+++ b/src/ui/torrent_table.js
@@ -12,55 +12,7 @@ const name_style = {
whiteSpace: 'nowrap'
};
-class TorrentTable extends Component {
- render() {
- const { selection, torrents, dispatch } = this.props;
- return (
-
- );
- }
-}
-
-class Torrent extends Component {
+class _Torrent extends Component {
shouldComponentUpdate(nextProps, _) {
const { selection, torrent } = this.props;
const nt = nextProps.torrent;
@@ -111,6 +63,60 @@ class Torrent extends Component {
}
}
+const Torrent = connect((state, props) => {
+ return {
+ torrent: state.torrents[props.id],
+ selection: state.selection,
+ };
+})(_Torrent);
+
+class TorrentTable extends Component {
+ render() {
+ const { selection, torrents, dispatch } = this.props;
+ return (
+
+ );
+ }
+}
+
export default connect(state => ({
torrents: state.torrents,
selection: state.selection,