Merge pull request #83 from jfurrow/feature/delete-torrent-data

Allow deleting data when removing torrent
This commit is contained in:
John Furrow
2016-07-15 21:21:06 -07:00
committed by GitHub
12 changed files with 190 additions and 74 deletions
+4 -3
View File
@@ -54,8 +54,8 @@ const TorrentActions = {
});
},
deleteTorrents: (hash) => {
return axios.post('/api/client/torrents/delete', {hash})
deleteTorrents: (hash, deleteData) => {
return axios.post('/api/client/torrents/delete', {hash, deleteData})
.then((json = {}) => {
return json.data;
})
@@ -64,7 +64,8 @@ const TorrentActions = {
type: ActionTypes.CLIENT_REMOVE_TORRENT_SUCCESS,
data: {
data,
count: hash.length
count: hash.length,
deleteData
}
});
})
@@ -0,0 +1,140 @@
import _ from 'lodash';
import classnames from 'classnames';
import {formatMessage, FormattedMessage, injectIntl} from 'react-intl';
import React from 'react';
import Checkbox from '../../General/FormElements/Checkbox';
import Modal from '../Modal';
import SettingsStore from '../../../stores/SettingsStore';
import stringUtil from '../../../../../shared/util/stringUtil';
import TorrentActions from '../../../actions/TorrentActions';
import TorrentStore from '../../../stores/TorrentStore';
const METHODS_TO_BIND = [
'handleRemovalConfirmation',
'handleCheckboxChange'
];
class RemoveTorrentsModal extends React.Component {
constructor() {
super();
this.state = {
deleteData: SettingsStore.getFloodSettings('deleteTorrentData')
};
METHODS_TO_BIND.forEach((method) => {
this[method] = this[method].bind(this);
});
}
getActions(torrents) {
if (torrents.length === 0) {
return [
{
clickHandler: null,
content: 'OK',
triggerDismiss: true,
type: 'primary'
}
];
}
return [
{
clickHandler: this.handleRemoveTorrentDecline,
content: this.props.intl.formatMessage({
id: 'button.no',
defaultMessage: 'No'
}),
triggerDismiss: true,
type: 'secondary'
},
{
clickHandler: this.handleRemovalConfirmation.bind(this, torrents),
content: this.props.intl.formatMessage({
id: 'button.yes',
defaultMessage: 'Yes'
}),
triggerDismiss: true,
type: 'primary'
}
];
}
getContent(torrents) {
let modalContent = null;
let deleteDataContent = null;
let selectedTorrentCount = torrents.length;
if (selectedTorrentCount === 0) {
modalContent = this.props.intl.formatMessage({
id: 'torrents.remove.error.no.torrents.selected',
defaultMessage: 'You haven\'t selected any torrents.'
});
} else {
let torrentText = stringUtil.pluralize('torrent', selectedTorrentCount);
modalContent = this.props.intl.formatMessage({
id: 'torrents.remove.are.you.sure',
defaultMessage: `Are you sure you want to remove {count, plural,
=0 {no torrents}
=1 {one torrent}
other {# torrents}
}?`
}, {
count: selectedTorrentCount
});
deleteDataContent = (
<div className="form__row">
<div className="form__column">
<Checkbox onChange={this.handleCheckboxChange}
checked={this.state.deleteData}>
<FormattedMessage
defaultMessage="Delete data"
id="torrents.remove.delete.data"
/>
</Checkbox>
</div>
</div>
);
}
return (
<div className="form modal__content">
<div className="form__row">
<div className="form__column">
{modalContent}
</div>
</div>
{deleteDataContent}
</div>
);
}
handleCheckboxChange(checkboxState) {
this.setState({deleteData: checkboxState});
}
handleRemovalConfirmation(torrents) {
TorrentActions.deleteTorrents(torrents, this.state.deleteData);
}
render() {
let selectedTorrents = TorrentStore.getSelectedTorrents() || [];
let modalHeading = this.props.intl.formatMessage({
id: 'torrents.remove',
defaultMessage: 'Remove Torrents'
});
return (
<Modal actions={this.getActions(selectedTorrents)}
alignment="center"
content={this.getContent(selectedTorrents)}
dismiss={this.props.dismiss}
heading={modalHeading} />
);
}
}
export default injectIntl(RemoveTorrentsModal);
@@ -7,6 +7,7 @@ import ConfirmModal from './ConfirmModal';
import EventTypes from '../../constants/EventTypes';
import Modal from './Modal';
import MoveTorrentsModal from './MoveTorrentsModal';
import RemoveTorrentsModal from './RemoveTorrentsModal';
import SetTagsModal from './SetTagsModal';
import SettingsModal from './SettingsModal';
import TorrentDetailsModal from './TorrentDetailsModal';
@@ -27,6 +28,7 @@ export default class Modals extends React.Component {
'add-torrents': AddTorrentsModal,
confirm: ConfirmModal,
'move-torrents': MoveTorrentsModal,
'remove-torrents': RemoveTorrentsModal,
'set-taxonomy': SetTagsModal,
settings: SettingsModal,
'torrent-details': TorrentDetailsModal
@@ -51,71 +51,9 @@ class ActionBar extends React.Component {
UIActions.displayModal({id: 'add-torrents'});
}
handleRemoveTorrentConfirm(torrents) {
TorrentActions.deleteTorrents(torrents);
}
handleRemoveTorrents() {
let selectedTorrents = TorrentStore.getSelectedTorrents() || [];
let selectedTorrentCount = selectedTorrents.length;
let actions = [
{
clickHandler: this.handleRemoveTorrentDecline,
content: this.props.intl.formatMessage({
id: 'button.no',
defaultMessage: 'No'
}),
triggerDismiss: true,
type: 'secondary'
},
{
clickHandler: this.handleRemoveTorrentConfirm.bind(this, selectedTorrents),
content: this.props.intl.formatMessage({
id: 'button.yes',
defaultMessage: 'Yes'
}),
triggerDismiss: true,
type: 'primary'
}
];
let content = this.props.intl.formatMessage({
id: 'torrents.remove.are.you.sure',
defaultMessage: `Are you sure you want to remove {count, plural,
=0 {no torrents}
=1 {one torrent}
other {# torrents}
}?`
}, {
count: selectedTorrentCount
});
if (selectedTorrentCount === 0) {
actions = [
{
clickHandler: null,
content: 'OK',
triggerDismiss: true,
type: 'primary'
}
];
content = this.props.intl.formatMessage({
id: 'torrents.remove.error.no.torrents.selected',
defaultMessage: 'You haven\'t selected any torrents.'
});
}
UIActions.displayModal({
id: 'confirm',
options: {
actions,
content,
heading: this.props.intl.formatMessage({
id: 'torrents.remove',
defaultMessage: 'Remove Torrents'
})
}
id: 'remove-torrents'
});
}
@@ -165,7 +103,6 @@ class ActionBar extends React.Component {
</nav>
);
}
}
export default injectIntl(ActionBar);
@@ -188,7 +188,7 @@ class TorrentListContainer extends React.Component {
TorrentActions.pauseTorrents(selectedTorrents);
break;
case 'remove':
TorrentActions.deleteTorrents(selectedTorrents);
UIActions.displayModal({id: 'remove-torrents'});
break;
case 'move':
UIActions.displayModal({id: 'move-torrents'});
+1
View File
@@ -203,6 +203,7 @@ export default {
=1 {one torrent}
other {# torrents}
}?`,
'torrents.remove.delete.data': 'Delete data',
'torrents.remove.error.no.torrents.selected': 'You haven\'t selected any torrents.',
'torrents.remove': 'Remove Torrents'
};
+5
View File
@@ -198,6 +198,11 @@ class TorrentStoreClass extends BaseStore {
}
handleRemoveTorrentsSuccess(response) {
SettingsStore.saveFloodSettings({
id: 'deleteTorrentData',
data: response.deleteData
});
NotificationStore.add({
accumulation: {
id: 'notification.torrent.remove',
+1
View File
@@ -19,6 +19,7 @@
"cookie-parser": "^1.4.3",
"d3": "^3.5.6",
"debug": "^2.2.0",
"del": "^2.2.1",
"es6-promise": "^3.2.1",
"events": "^1.1.0",
"express": "^4.14.0",
-1
View File
@@ -1,6 +1,5 @@
'use strict';
let fs = require('fs');
let mv = require('mv');
let path = require('path');
let util = require('util');
+4
View File
@@ -61,6 +61,10 @@ class Torrent {
this._torrentData = this.getCalculatedClientData(clientData, opts);
}
get basePath() {
return this._torrentData.basePath;
}
get data() {
// TODO: Only return the properties that are different than the last time
// get was called. Perhaps identify the last time it was called by ID so
+27 -4
View File
@@ -1,6 +1,6 @@
'use strict';
let fs = require('fs');
let del = require('del');
let util = require('util');
let clientResponseUtil = require('../util/clientResponseUtil');
@@ -64,11 +64,34 @@ var client = {
request.send();
},
deleteTorrents: (hashes, callback) => {
deleteTorrents: (options, callback) => {
let files = [];
let request = new ClientRequest();
request.add('removeTorrents', {hashes});
request.onComplete(callback);
if (options.deleteData) {
let torrents = torrentCollection.torrents;
files = options.hashes.reduce((memo, hash) => {
let filePath = torrents[hash].basePath;
// Let's not try to delete these files.
if (filePath != null && filePath !== '/' && filePath !== ''
&& filePath !== '.') {
memo.push(filePath);
}
return memo;
}, []);
}
request.add('removeTorrents', {hashes: options.hashes});
request.onComplete((response, error) => {
if (options.deleteData && files.length > 0) {
del(files, {force: true});
}
callback(response, error);
});
request.send();
},
+4 -1
View File
@@ -69,7 +69,10 @@ router.post('/torrents/move', function(req, res, next) {
});
router.post('/torrents/delete', function(req, res, next) {
client.deleteTorrents(req.body.hash, ajaxUtil.getResponseFn(res));
let deleteData = req.body.deleteData;
let hashes = req.body.hash;
client.deleteTorrents({hashes, deleteData}, ajaxUtil.getResponseFn(res));
});
router.get('/torrents/taxonomy', function(req, res, next) {