mirror of
https://github.com/zoriya/flood.git
synced 2026-06-01 10:35:59 +00:00
Merge pull request #83 from jfurrow/feature/delete-torrent-data
Allow deleting data when removing torrent
This commit is contained in:
@@ -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'});
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -198,6 +198,11 @@ class TorrentStoreClass extends BaseStore {
|
||||
}
|
||||
|
||||
handleRemoveTorrentsSuccess(response) {
|
||||
SettingsStore.saveFloodSettings({
|
||||
id: 'deleteTorrentData',
|
||||
data: response.deleteData
|
||||
});
|
||||
|
||||
NotificationStore.add({
|
||||
accumulation: {
|
||||
id: 'notification.torrent.remove',
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
let fs = require('fs');
|
||||
let mv = require('mv');
|
||||
let path = require('path');
|
||||
let util = require('util');
|
||||
|
||||
@@ -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
@@ -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();
|
||||
},
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user