From 6983703672ac3459f6ae722edc0c1090be6bd305 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 25 Aug 2017 09:43:54 -0400 Subject: [PATCH] Refactor torrent selection state management Now reduxified and more consistent --- src/actions/selection.js | 74 ++++++++++++++++++++++++++++++++ src/reducers/filter_subscribe.js | 4 +- src/reducers/index.js | 2 + src/reducers/selection.js | 14 ++++++ src/torrent_state.js | 71 ------------------------------ src/ui/torrent_details.js | 16 +++---- src/ui/torrent_table.js | 22 +++++----- 7 files changed, 109 insertions(+), 94 deletions(-) create mode 100644 src/actions/selection.js create mode 100644 src/reducers/selection.js delete mode 100644 src/torrent_state.js diff --git a/src/actions/selection.js b/src/actions/selection.js new file mode 100644 index 0000000..06e2c72 --- /dev/null +++ b/src/actions/selection.js @@ -0,0 +1,74 @@ +import { filter_subscribe, filter_unsubscribe } from './filter_subscribe'; +import { unsubscribe } from './subscribe'; +import { push } from 'react-router-redux'; + +export const UNION = 'UNION'; +export const SUBTRACT = 'SUBTRACT'; +export const EXCLUSIVE = 'EXCLUSIVE'; + +Set.prototype.difference = function(set) { + var diff = new Set(this); + for (var v of set) { + diff.delete(v); + } + return diff; +} + +export default function selectTorrent(id, action) { + return (dispatch, getState) => { + const previous = new Set(getState().selection); + dispatch({ type: action, id }); + + const state = getState(); + const next = new Set(state.selection); + const filter_subscriptions = state.filter_subscribe; + const subscriptions = state.subscribe; + const { peers, files, pieces, trackers } = state; + + const added = next.difference(previous); + const removed = previous.difference(next); + + added.forEach(t => { + const criteria = [ + { field: "torrent_id", op: "==", value: id } + ]; + dispatch(filter_subscribe("peer", criteria)); + dispatch(filter_subscribe("file", criteria)); + dispatch(filter_subscribe("piece", criteria)); + dispatch(filter_subscribe("tracker", criteria)); + }); + + removed.forEach(t => { + /* Remove filter subscriptions */ + const serials = filter_subscriptions + .filter(sub => sub.criteria[0] && sub.criteria[0].value === t) + .map(sub => sub.serial); + serials.forEach(serial => dispatch(filter_unsubscribe(serial))); + /* Remove resource subscriptions */ + const ids = [ + ...Object.values(files) + .filter(file => file.torrent_id === t) + .map(file => file.id) + //...Object.values(peers) + // .filter(peer => peer.torrent_id === t) + // .map(peer => peer.id), + //...Object.values(trackers) + // .filter(tracker => tracker.torrent_id === t) + // .map(tracker => tracker.id), + //...Object.values(pieces) + // .filter(piece => piece.torrent_id === t) + // .map(piece => piece.id), + ]; + if (ids.length > 0) { + dispatch(unsubscribe(...ids)); + } + }); + + const url_torrents = state.selection.slice(0, 3); + if (url_torrents.length > 0) { + dispatch(push(`/torrents/${url_torrents}`)); + } else { + dispatch(push("/")); + } + }; +} diff --git a/src/reducers/filter_subscribe.js b/src/reducers/filter_subscribe.js index dca672e..2826dbf 100644 --- a/src/reducers/filter_subscribe.js +++ b/src/reducers/filter_subscribe.js @@ -17,8 +17,8 @@ export default function filter_subscribe(state = [], action) { ]; } case FILTER_UNSUBSCRIBE: { - const { _serial } = action; - return state.filter(({ serial }) => serial !== _serial); + const { serial } = action; + return state.filter(filter => filter.serial !== serial); } } return state; diff --git a/src/reducers/index.js b/src/reducers/index.js index d22999c..6fd06f2 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,5 +1,6 @@ import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux' +import selection from './selection'; import subscribe from './subscribe'; import filter_subscribe from './filter_subscribe'; import server from './server'; @@ -7,6 +8,7 @@ import torrents from './torrents'; import files from './files'; const root = combineReducers({ + selection, subscribe, filter_subscribe, server, diff --git a/src/reducers/selection.js b/src/reducers/selection.js new file mode 100644 index 0000000..6c83c39 --- /dev/null +++ b/src/reducers/selection.js @@ -0,0 +1,14 @@ +import { UNION, SUBTRACT, EXCLUSIVE } from '../actions/selection'; + +export default function selection(state = [], action) { + const { id } = action; + switch (action.type) { + case UNION: + return [id, ...state.filter(t => t !== id)]; + case SUBTRACT: + return state.filter(t => t !== id); + case EXCLUSIVE: + return [id]; + } + return state; +} diff --git a/src/torrent_state.js b/src/torrent_state.js deleted file mode 100644 index b4128d9..0000000 --- a/src/torrent_state.js +++ /dev/null @@ -1,71 +0,0 @@ -import { filter_subscribe, filter_unsubscribe } from './actions/filter_subscribe'; -import { push } from 'react-router-redux'; -/* Listen pal, we're just gonna pretend this import isn't happening */ -import { dispatch, getState } from './store'; - -export function activeTorrents() { - const { pathname } = getState().router.location; - if (pathname.indexOf("/torrents/") !== 0) { - return []; - } else { - return pathname.slice("/torrents/".length).split(","); - } -} - -export function updateSubscriptions(added, removed) { - if (added.length > 0) { - added.forEach(t => { - const criteria = [ - { field: "torrent_id", op: "==", value: t } - ]; - dispatch(filter_subscribe("peer", criteria)); - dispatch(filter_subscribe("file", criteria)); - dispatch(filter_subscribe("piece", criteria)); - dispatch(filter_subscribe("tracker", criteria)); - }); - } - if (removed.length > 0) { - const subscriptions = getState().filter_subscribe; - removed.forEach(t => { - const serials = subscriptions - .filter(sub => sub.criteria[0] && sub.criteria[0].value == t) - .map(sub => sub.serial); - serials.forEach(serial => dispatch(filter_unsubscribe(serial))); - }); - } -} - -export const selectop = { - EXCLUSIVE: 1, - UNION: 2, - SUBTRACT: 3 -}; - -export function selectTorrent(t, action = UNION) { - let active = activeTorrents(getState().router); - let removed = [], added = []; - switch (action) { - case selectop.EXCLUSIVE: - removed = active.slice(); - active = [t.id]; - added = [t.id]; - break; - case selectop.UNION: - if (active.indexOf(t.id) === -1) { - active = [...active, t.id]; - added = [t.id]; - } - break; - case selectop.SUBTRACT: - if (active.indexOf(t.id) !== -1) { - removed = [t.id]; - active = active.filter(a => a != t.id); - } - break; - } - const url = active.length === 0 ? "/" : `/torrents/${active.join(',')}`; - if (url !== getState().router.location) { - dispatch(push(url)); - } - updateSubscriptions(added, removed); -} diff --git a/src/ui/torrent_details.js b/src/ui/torrent_details.js index 1df01ea..38d58b2 100644 --- a/src/ui/torrent_details.js +++ b/src/ui/torrent_details.js @@ -1,6 +1,5 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { activeTorrents } from '../torrent_state'; import FontAwesome from 'react-fontawesome'; import { ButtonGroup, @@ -153,10 +152,10 @@ class TorrentDetails extends Component { }; } - renderHeader(active) { + renderHeader(selection) { return (
-

{active.length} torrents

+

{selection.length} torrents

@@ -178,16 +177,14 @@ class TorrentDetails extends Component { } render() { - const active = activeTorrents(); - const { torrents } = this.props; - const { files } = this.props; + const { torrents, files, selection } = this.props; const _files = Object.values(files).reduce((s, f) => ({ ...s, [f.torrent_id]: [...(s[f.torrent_id] || []), f] }), {}); return (
- {active.length > 1 ? this.renderHeader(active) : null} - {active.map(id => 1 ? this.renderHeader(selection) : null} + {selection.map(id => )} @@ -199,5 +196,6 @@ class TorrentDetails extends Component { export default connect(state => ({ router: state.router, torrents: state.torrents, - files: state.files + files: state.files, + selection: state.selection }))(TorrentDetails); diff --git a/src/ui/torrent_table.js b/src/ui/torrent_table.js index 5f3ef83..aae4d16 100644 --- a/src/ui/torrent_table.js +++ b/src/ui/torrent_table.js @@ -1,12 +1,11 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { activeTorrents, selectTorrent, selectop } from '../torrent_state'; +import selectTorrent, { UNION, SUBTRACT, EXCLUSIVE } from '../actions/selection'; import { formatBitrate } from '../bitrate'; class TorrentTable extends Component { render() { - const { torrents } = this.props; - const active = activeTorrents(); + const { selection, torrents, dispatch } = this.props; return ( @@ -25,7 +24,7 @@ class TorrentTable extends Component { className={`torrent progress-row ${ t.status } ${ - active.indexOf(t.id) !== -1 ? "selected" : "" + selection.indexOf(t.id) !== -1 ? "selected" : "" }`} style={{ backgroundSize: `${t.progress * 100}% 3px` @@ -34,22 +33,20 @@ class TorrentTable extends Component { @@ -64,5 +61,6 @@ class TorrentTable extends Component { export default connect(state => ({ torrents: state.torrents, + selection: state.selection, router: state.router }))(TorrentTable);
- selectTorrent(t, - e.target.checked ? selectop.UNION : selectop.SUBTRACT)} + dispatch(selectTorrent(t.id, e.target.checked ? UNION : SUBTRACT)) + } /> { e.preventDefault(); - selectTorrent(t, selectop.EXCLUSIVE); + dispatch(selectTorrent(t.id, EXCLUSIVE)); }} - > - {t.name} - + >{t.name} {formatBitrate(t.rate_up)} {formatBitrate(t.rate_down)}