Add actions to torrents

This commit is contained in:
CzBiX
2019-04-13 04:21:07 +08:00
parent acc194654a
commit 39797b900d
7 changed files with 199 additions and 46 deletions

View File

@@ -38,10 +38,11 @@ class Api {
}
public getMainData(rid?: number) {
const params = {
rid,
};
return this.axios.get('/sync/maindata', {
params: {
rid,
},
params,
});
}
@@ -66,6 +67,27 @@ class Api {
return this.axios.get('/log/main').then(this.handleResponse);
}
public deleteTorrents(hashes: string[], deleteFiles: boolean) {
return this.actionTorrents('delete', hashes, {deleteFiles});
}
public pauseTorrents(hashes: string[]) {
return this.actionTorrents('pause', hashes);
}
public resumeTorrents(hashes: string[]) {
return this.actionTorrents('resume', hashes);
}
private actionTorrents(action: string, hashes: string[], extra?: any) {
const params: any = {
hashes: hashes.join('|'),
...extra,
};
const data = new URLSearchParams(params);
return this.axios.post('/torrents/' + action, data).then(this.handleResponse);
}
private handleResponse(resp: AxiosResponse) {
return resp.data;
}

View File

@@ -9,6 +9,7 @@
</v-navigation-drawer>
<v-toolbar
:clipped-left="$vuetify.breakpoint.lgAndUp"
:scroll-toolbar-off-screen="!$vuetify.breakpoint.lgAndUp"
app
>
<v-toolbar-title class="headline">

View File

@@ -37,6 +37,7 @@
:rules="[v => !!v || 'URLs is required']"
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
required
autofocus
/>
</v-flex>
<v-flex>
@@ -128,6 +129,7 @@ export default Vue.extend({
this.submitting = false;
this.dialog = false;
this.params.urls = null;
},
},
});

View File

@@ -48,11 +48,11 @@
<v-btn
@click="submit"
color="primary"
:disabled="!valid"
:disabled="!valid || submitting"
>
<v-progress-circular
v-if="submitting"
:indeterminate="true"
indeterminate
/>
<template v-else>
Submit

View File

@@ -1,52 +1,79 @@
<template>
<v-data-table
:headers="headers"
:items="torrents"
item-key="hash"
:hide-actions="torrents.length <= pagination.rowsPerPage"
:pagination.sync="pagination"
>
<template v-slot:items="row">
<td>
<v-checkbox
:input-value="row.selected"
hide-details
/>
</td>
<td>{{ row.item.name }}</td>
<td>{{ row.item.size | formatSize }}</td>
<td>
<v-progress-linear
height="1.5em"
:value="row.item.progress * 100"
class="text-xs-center ma-0"
>
<span :class="row.item.progress | progressColorClass">{{ row.item.progress | progressText }}</span>
</v-progress-linear>
</td>
<td>{{ row.item.state }}</td>
<td>{{ row.item.num_seeds }}/{{ row.item.num_complete }}</td>
<td>{{ row.item.num_leechs }}/{{ row.item.num_incomplete }}</td>
<td>{{ row.item.dlspeed | formatSize }}/s</td>
<td>{{ row.item.upspeed | formatSize }}/s</td>
<td>{{ row.item.eta | formatDuration }}</td>
<td>{{ row.item.ratio.toFixed(2) }}</td>
<td>{{ row.item.added_on | formatTimestamp }}</td>
</template>
</v-data-table>
<div>
<v-toolbar
flat
dense
color="white"
>
<v-btn icon :disabled="!hasSelected" @click="confirmDelete">
<v-icon>mdi-delete</v-icon>
</v-btn>
<v-divider vertical inset />
<v-btn icon :disabled="!hasSelected" @click="resumeTorrents">
<v-icon>mdi-play</v-icon>
</v-btn>
<v-btn icon :disabled="!hasSelected" @click="pauseTorrents">
<v-icon>mdi-pause</v-icon>
</v-btn>
</v-toolbar>
<v-data-table
:headers="headers"
:items="torrents"
item-key="hash"
:hide-actions="torrents.length <= pagination.rowsPerPage"
select-all
:pagination.sync="pagination"
v-model="selectedRows"
>
<template v-slot:items="row">
<td>
<v-checkbox
v-model="row.selected"
hide-details
/>
</td>
<td>{{ row.item.name }}</td>
<td>{{ row.item.size | formatSize }}</td>
<td>
<v-progress-linear
height="1.4em"
:value="row.item.progress * 100"
class="text-xs-center ma-0"
>
<span :class="row.item.progress | progressColorClass">{{ row.item.progress | progressText }}</span>
</v-progress-linear>
</td>
<td>{{ row.item.state }}</td>
<td>{{ row.item.num_seeds }}/{{ row.item.num_complete }}</td>
<td>{{ row.item.num_leechs }}/{{ row.item.num_incomplete }}</td>
<td>{{ row.item.dlspeed | formatSize }}/s</td>
<td>{{ row.item.upspeed | formatSize }}/s</td>
<td>{{ row.item.eta | formatDuration }}</td>
<td>{{ row.item.ratio.toFixed(2) }}</td>
<td>{{ row.item.added_on | formatTimestamp }}</td>
</template>
</v-data-table>
<confirm-delete-dialog v-if="deleteDialog" v-model="deleteDialog" :torrents="selectedRows" />
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import ConfirmDeleteDialog from './dialogs/ConfirmDeleteDialog.vue';
import { mapState, mapGetters } from 'vuex';
import _ from 'lodash';
import { api } from '../Api';
export default Vue.extend({
name: 'torrents',
components: {
ConfirmDeleteDialog,
},
data() {
const headers = [
{ text: '', value: '#', sortable: false },
{ text: 'Name', value: 'name', class: 'th-name' },
{ text: 'Size', value: 'size' },
{ text: 'Progress', value: 'progress' },
@@ -62,6 +89,8 @@ export default Vue.extend({
return {
headers,
selectedRows: [],
deleteDialog: false,
pagination: {
rowsPerPage: 100,
},
@@ -78,6 +107,12 @@ export default Vue.extend({
'torrentGroupByCategory',
'torrentGroupBySite',
]),
hasSelected() {
return this.selectedRows.length;
},
selectedHashes() {
return this.selectedRows.map(_.property('hash'));
},
torrents() {
if (!this.isDataReady) {
return [];
@@ -96,10 +131,6 @@ export default Vue.extend({
filters: {
progressText(progress: number) {
// if (progress >= 1) {
// return null;
// }
return Math.floor(progress * 100) + '%';
},
progressColorClass(progress: number) {
@@ -107,6 +138,18 @@ export default Vue.extend({
return color + '--text';
},
},
methods: {
confirmDelete() {
this.deleteDialog = true;
},
async resumeTorrents() {
await api.resumeTorrents(this.selectedHashes);
},
async pauseTorrents() {
await api.pauseTorrents(this.selectedHashes);
},
},
});
</script>

View File

@@ -0,0 +1,78 @@
<template>
<v-dialog :value="value" @input="$emit('input', $event)" width="40em">
<v-card>
<v-card-title
class="headline grey lighten-4"
>
<v-icon class="mr-2">mdi-delete</v-icon>
<span>Delete torrents</span>
</v-card-title>
<v-card-text>
Are you sure you want to delete the selected torrents from the transfer list?
<ol class="torrents pt-4">
<li v-for="(row, i) in torrents" :key="i">
{{ row.name }}
</li>
</ol>
<v-checkbox
v-model="deleteFiles"
prepend-icon="mdi-file-cancel"
label="Also delete the files on the hard disk"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn flat @click="closeDialog">Cancel</v-btn>
<v-btn
@click="submit"
color="warning"
:disabled="submitting"
>
<v-progress-circular
v-if="submitting"
indeterminate
/>
<template v-else>
Delete
</template>
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import Vue from 'vue'
import { api } from '@/Api';
export default Vue.extend({
props: {
torrents: Array,
value: Boolean,
},
data() {
return {
deleteFiles: false,
submitting: false,
};
},
methods: {
closeDialog() {
this.$emit('input', false);
},
async submit() {
if (this.submitting) {
return;
}
this.submitting = true;
const hashed = this.torrents.map((t: any) => t.hash);
await api.deleteTorrents(hashed, this.deleteFiles);
this.closeDialog();
},
},
});
</script>

View File

@@ -24,7 +24,14 @@ export default new Vuex.Store({
if (payload.full_update) {
state.mainData = payload;
} else {
state.mainData = _.merge({}, state.mainData, payload);
const tmp: any = _.cloneDeep(state.mainData);
if (payload.torrents_removed) {
for (const hash of payload.torrents_removed) {
delete tmp.torrents[hash];
}
delete payload.torrents_removed;
}
state.mainData = _.merge(tmp, payload);
}
},
updatePreferences(state, payload) {