Files
qb-web/src/components/AddForm.vue
2019-05-10 19:25:54 +08:00

273 lines
7.0 KiB
Vue

<template>
<div>
<v-btn
fab
bottom
color="primary"
fixed
right
@click="dialog = !dialog"
class="btn-add"
:class="{'with-footer': $vuetify.breakpoint.smAndUp}"
>
<v-icon>mdi-link-plus</v-icon>
</v-btn>
<v-dialog v-model="dialog" persistent width="50em">
<v-card>
<v-card-title
class="headline grey lighten-4"
>
<v-icon class="mr-2">mdi-link-plus</v-icon>
<span>Add Torrents</span>
</v-card-title>
<v-card-text>
<v-form
v-model="valid"
ref="form"
>
<v-container pa-0 v-bind="{ [`grid-list-${$vuetify.breakpoint.name}`]: true }">
<v-layout wrap>
<v-flex
xs12
ref="fileZone"
>
<div
v-show="files.length"
class="files grey lighten-4 align-center justify-space-between subheading font-weight-medium pl-2"
@click="selectFiles"
>
<input ref="file" type="file" multiple class="d-none" @change="onFilesChanged">
<span v-if="files.length == 1">Selected file: {{ files[0].name }}</span>
<span v-else>Selected {{ files.length }} files.</span>
<v-btn icon @click.stop="files = []">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
<v-textarea
v-show="!files.length"
label="URLs"
hint="One link per line"
:placeholder="'Upload torrents by drop them here,\nor click attachment button at right to select.'"
prepend-icon="mdi-link"
append-outer-icon="mdi-attachment"
:rules="[v => (!!files.length || !!v || 'URLs is required')]"
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
required
autofocus
:value="params.urls"
@input="setParams('urls', $event)"
@click:append-outer="selectFiles"
/>
</v-flex>
<v-flex>
<v-combobox
label="Category"
prepend-icon="mdi-folder"
clearable
hide-no-data
:items="categories"
:value="params.category"
@input="setParams('category', $event)"
/>
</v-flex>
<v-flex>
<v-checkbox
v-model="autoStart"
label="Start torrent"
prepend-icon="mdi-play-pause"
/>
</v-flex>
<v-flex>
<v-checkbox
prepend-icon="mdi-progress-check"
label="Skip hash check"
:value="params.skip_checking"
@change="setParams('skip_checking', $event)"
/>
</v-flex>
</v-layout>
</v-container>
</v-form>
<v-alert
type="warning"
:value="error"
v-text="error"
/>
</v-card-text>
<v-card-actions>
<!-- <v-btn flat>More</v-btn> -->
<v-spacer />
<v-btn flat @click="dialog = false">Cancel</v-btn>
<v-btn
flat
@click="submit"
color="primary"
:disabled="!valid"
:loading="submitting"
>
Submit
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script lang="ts">
import _ from 'lodash';
import Vue from 'vue';
import { mapState } from 'vuex';
import { api } from '../Api';
const defaultParams = {
urls: null,
category: null,
paused: false,
skip_checking: false,
};
export default Vue.extend({
props: {
url: String,
},
data() {
return {
dialog: false,
valid: false,
files: [],
userParams: {},
error: null,
submitting: false,
};
},
computed: {
...mapState({
prefs: 'preferences',
categories(state, getters) {
return Object.keys(getters.torrentGroupByCategory).filter(_.identity);
},
}),
params() {
return Object.assign({}, defaultParams, this.userParams);
},
autoStart: {
get(): boolean {
return !this.params.paused;
},
set(value: boolean) {
const paused = !value;
const tmp = defaultParams.paused === paused ? null : paused;
this.setParams('paused', tmp);
},
},
},
created() {
defaultParams.paused = this.prefs.start_paused_enabled;
},
mounted() {
this.$refs.fileZone.addEventListener('drop', this.onDrop, true);
},
beforeDestroy() {
this.$refs.fileZone.removeEventListener('drop', this.onDrop, true);
},
methods: {
setParams(key: string, value: any){
if (_.isNil(value)) {
Vue.delete(this.userParams, key);
} else {
Vue.set(this.userParams, key, value);
}
},
async submit() {
if (this.submitting) {
return;
}
this.submitting = true;
this.error = null;
let files;
if (this.files.length) {
files = this.files;
Vue.delete(this.userParams, 'urls');
} else {
files = null;
}
try {
const resp = await api.addTorrents(this.userParams, files);
if (resp !== 'Ok.') {
this.error = resp;
}
} catch (e) {
this.error = e.message
}
this.submitting = false;
if (this.error) {
return;
}
this.dialog = false;
Vue.delete(this.userParams, 'urls');
this.files = [];
this.$refs.form.resetValidation();
},
selectFiles() {
this.$refs.file.click();
},
onFilesChanged() {
this.files = this.$refs.file.files;
},
onDrop(e: DragEvent) {
const transfer = e.dataTransfer!;
const files = transfer.files;
if (!files.length) {
return;
}
e.preventDefault();
this.files = files;
},
},
watch: {
url(v) {
if (!v) {
return;
}
if (!this.dialog) {
Vue.set(this.userParams, 'urls', v);
this.dialog = true;
}
this.$emit('input', null);
},
files(v) {
this.$refs.form.validate();
},
},
});
</script>
<style lang="scss" scoped>
.btn-add.with-footer {
margin-bottom: 36px;
}
.files {
display: flex;
height: 3em;
border: 1px dashed;
border-color: grey !important;
border-radius: 2px;
}
</style>