This commit is contained in:
CzBiX
2020-03-25 18:40:58 +08:00
parent 145bbb3268
commit 93baf53562
17 changed files with 519 additions and 523 deletions

View File

@@ -23,7 +23,7 @@
class="headline grey lighten-4"
>
<v-icon class="mr-2">mdi-link-plus</v-icon>
<span>Add Torrents</span>
<span>{{ $t('title.add_torrents') }}</span>
</v-card-title>
<v-card-text class="pb-0">
<v-form
@@ -42,16 +42,16 @@
multiple
chips
outlined
label="Files"
:label="$t('files')"
/>
<v-textarea
v-show="!files.length"
label="URLs"
hint="One link per line"
:placeholder="placeholder"
label="URL"
:hint="$t('dialog.add_torrents.hint')"
:placeholder="$t('dialog.add_torrents.placeholder')"
prepend-icon="mdi-link"
append-outer-icon="mdi-attachment"
:rules="[v => (!!files.length || !!v || 'URLs is required')]"
:rules="[v => (!!files.length || !!v || $t('msg.item_is_required', { item: 'URL' }))]"
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
required
:autofocus="!phoneLayout"
@@ -65,7 +65,7 @@
<template v-if="showMore">
<v-col cols="12" sm="6">
<v-combobox
label="Category"
:label="$t('category', 1)"
prepend-icon="mdi-folder"
clearable
hide-no-data
@@ -77,7 +77,7 @@
<v-col cols="12" sm="6">
<v-checkbox
prepend-icon="mdi-file-tree"
label="Create subfolder"
:label="$t('label.create_subfolder')"
:input-value="true"
@change="setParams('root_path', $event)"
/>
@@ -86,14 +86,14 @@
<v-col cols="12" sm="6">
<v-checkbox
v-model="autoStart"
label="Start torrent"
:label="$t('label.start_torrent')"
prepend-icon="mdi-play-pause"
/>
</v-col>
<v-col cols="12" sm="6">
<v-checkbox
prepend-icon="mdi-progress-check"
label="Skip hash check"
:label="$t('label.skip_hash_check')"
:input-value="params.skip_checking"
@change="setParams('skip_checking', $event)"
/>
@@ -101,7 +101,7 @@
<template v-if="showMore">
<v-col cols="12" sm="6">
<v-checkbox
label="In sequential order"
:label="$t('label.in_sequential_order')"
prepend-icon="mdi-sort-descending"
:ipnut-value="params.sequentialDownload"
@change="setParams('sequentialDownload', $event.value)"
@@ -110,7 +110,7 @@
<v-col cols="12" sm="6">
<v-checkbox
prepend-icon="mdi-ray-start-end"
label="First and last pieces first"
:label="$t('label.first_and_last_pieces_first')"
:input-value="params.firstLastPiecePrio"
@change="setParams('firstLastPiecePrio', $event)"
/>
@@ -126,9 +126,9 @@
/>
</v-card-text>
<v-card-actions>
<v-btn text @click="showMore = !showMore" v-text="showMore ? 'Less' : 'More'" />
<v-btn text @click="showMore = !showMore" v-text="showMore ? $t('less') : $t('more')" />
<v-spacer />
<v-btn text @click="dialog = false">Cancel</v-btn>
<v-btn text @click="dialog = false">{{ $t('cancel') }}</v-btn>
<v-btn
text
@click="submit"
@@ -136,7 +136,7 @@
:disabled="!valid"
:loading="submitting"
>
Submit
{{ $t('submit') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -148,6 +148,8 @@
import _ from 'lodash';
import Vue from 'vue';
import { mapState } from 'vuex';
import { tr } from '@/locale';
import api from '../Api';
const defaultParams = {
@@ -160,10 +162,21 @@ const defaultParams = {
firstLastPiecePrio: false,
};
export default Vue.extend({
data() {
interface Data {
tr: any,
dialog: boolean,
valid: boolean,
files: any[],
userParams: any,
error: string | null,
submitting: boolean,
showMore: boolean,
}
export default {
data(): Data {
return {
placeholder: 'Upload torrents by drop them here,\nor click attachment button at right to select.',
tr,
dialog: false,
valid: false,
files: [],
@@ -180,7 +193,7 @@ export default Vue.extend({
}),
...mapState({
categories(state, getters) {
return getters.allCategories.map(c => ({ text: c.name, value: c.key }));
return getters.allCategories.map((c: any) => ({ text: c.name, value: c.key }));
},
}),
params() {
@@ -289,7 +302,7 @@ export default Vue.extend({
this.$refs.form.validate();
},
},
});
};
</script>
<style lang="scss" scoped>

View File

@@ -5,23 +5,9 @@
class="drawer"
>
<template v-for="item in items">
<v-row
v-if="item.heading"
:key="item.heading"
align="center"
>
<v-col cols="6">
<v-subheader v-if="item.heading">
{{ item.heading }}
</v-subheader>
</v-col>
<v-col cols="6" class="text-center">
<a href="#!" class="body-2 black--text">EDIT</a>
</v-col>
</v-row>
<v-list-group
v-else-if="item.children"
:key="item.text"
v-if="item.children"
:key="item.title"
v-model="item.model"
:prepend-icon="item.model ? item.icon : item['icon-alt']"
append-icon=""
@@ -29,7 +15,7 @@
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>
{{ item.text }}
{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</template>
@@ -43,7 +29,7 @@
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ child.text }}
{{ child.title }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
@@ -55,13 +41,13 @@
:group="child"
/>
</template>
<v-list-item v-else :key="item.text" @click="item.click ? item.click() : null">
<v-list-item v-else :key="item.title" @click="item.click ? item.click() : null">
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ item.text }}
{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
@@ -73,6 +59,9 @@
import _ from 'lodash';
import Vue from 'vue';
import { mapState, mapMutations, mapGetters } from 'vuex';
import { tr } from '@/locale';
import { Torrent } from '@/types';
import FilterGroup from './drawer/FilterGroup.vue';
import api from '../Api';
import { formatSize } from '../filters';
@@ -80,47 +69,68 @@ import { SiteMap, StateType, AllStateTypes } from '../consts';
const stateList = [
{
title: 'Downloading',
title: tr('state.downloading'),
state: StateType.Downloading,
icon: 'download',
},
{
title: 'Seeding',
title: tr('state.seeding'),
state: StateType.Seeding,
icon: 'upload',
},
{
title: 'Completed',
title: tr('state.completed'),
state: StateType.Completed,
icon: 'check',
},
{
title: 'Resumed',
title: tr('state.resumed'),
state: StateType.Resumed,
icon: 'play',
},
{
title: 'Paused',
title: tr('state.paused'),
state: StateType.Paused,
icon: 'pause',
},
{
title: 'Active',
title: tr('state.active'),
state: StateType.Active,
icon: 'filter',
},
{
title: 'Inactive',
title: tr('state.inactive'),
state: StateType.Inactive,
icon: 'filter-outline',
},
{
title: 'Errored',
title: tr('state.errored'),
state: StateType.Errored,
icon: 'alert',
},
];
interface MenuItem {
icon: string,
'icon-alt'?: string,
title: string,
model?: boolean,
select?: string,
click?: () => void,
children?: MenuChildrenItem[],
}
interface MenuChildrenItem extends MenuItem {
key: string | null,
append?: string,
}
interface Data {
tr: any,
basicItems: MenuItem[],
endItems: MenuItem[],
}
export default {
components: {
FilterGroup,
@@ -130,21 +140,20 @@ export default {
value: Object,
},
data() {
return {
basicItems: null,
endItems: null,
};
},
data(): Data {
const basicItems = [
{ icon: 'mdi-settings', title: tr('settings'), click: () => alert(tr('todo')) },
];
const endItems = [
{ icon: 'mdi-delta', title: tr('logs'), click: () => this.updateOptions('showLogs', true) },
{ icon: 'mdi-history', title: tr('label.switch_to_old_ui'), click: this.switchUi },
];
created() {
this.basicItems = [
{ icon: 'mdi-settings', text: 'Settings', click: () => alert('TODO') },
];
this.endItems = [
{ icon: 'mdi-delta', text: 'Logs', click: () => this.updateOptions('showLogs', true) },
{ icon: 'mdi-history', text: 'Switch to old UI', click: this.switchUi },
];
return {
tr,
basicItems,
endItems,
};
},
computed: {
@@ -161,7 +170,7 @@ export default {
return _.concat(this.basicItems, this.endItems);
}
const filterGroups = [];
const filterGroups: MenuItem[] = [];
const totalSize = formatSize(_.sumBy(this.allTorrents, 'size'));
const states = stateList.map((item) => {
@@ -179,12 +188,12 @@ export default {
filterGroups.push({
icon: 'mdi-menu-up',
'icon-alt': 'mdi-menu-down',
title: 'State',
title: tr('state._'),
model: false,
select: 'state',
children: [
{
icon: 'mdi-filter-remove', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
icon: 'mdi-filter-remove', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
},
...states,
],
@@ -192,7 +201,7 @@ export default {
const categories: any[] = [{
key: '',
name: 'Uncategorized',
name: tr('uncategorized'),
}].concat(this.allCategories).map((category) => {
let value = this.torrentGroupByCategory[category.key];
if (_.isUndefined(value)) {
@@ -208,12 +217,12 @@ export default {
filterGroups.push({
icon: 'mdi-menu-up',
'icon-alt': 'mdi-menu-down',
title: 'Categories',
title: tr('category', 0),
model: !this.$vuetify.breakpoint.xsOnly,
select: 'category',
children: [
{
icon: 'mdi-folder-open', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
icon: 'mdi-folder-open', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
},
...categories,
],
@@ -222,7 +231,7 @@ export default {
const sites: any[] = _.sortBy(Object.entries(this.torrentGroupBySite).map(([key, value]) => {
const size = formatSize(_.sumBy(value, 'size'));
const site = (SiteMap as any)[key];
const title = `${site ? site.name : (key || 'Others')} (${value.length})`;
const title = `${site ? site.name : (key || tr('others'))} (${value.length})`;
const icon = _.defaultTo(site ? site.icon : null, 'mdi-server');
const append = `[${size}]`;
return {
@@ -232,18 +241,18 @@ export default {
filterGroups.push({
icon: 'mdi-menu-up',
'icon-alt': 'mdi-menu-down',
title: 'Sites',
title: tr('sites'),
model: false,
select: 'site',
children: [
{
icon: 'mdi-server', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
icon: 'mdi-server', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
},
...sites,
],
});
return _.concat(this.basicItems, [{ filterGroups }], this.endItems);
return _.concat(this.basicItems, [{filterGroups}] as any, this.endItems);
},
},

View File

@@ -27,21 +27,23 @@ import Vue from 'vue';
import {
computed, ref, watch, Ref,
} from '@vue/composition-api';
import { tr } from '@/locale';
import { DialogType, DialogConfig } from '@/store/types';
import { useMutations, useState } from '@/store';
import { timeout } from '@/utils';
const BUTTONS = {
[DialogType.Alert]: [
['Close', false],
[tr('close'), false],
],
[DialogType.YesNo]: [
['No', false],
['Yes', true],
[tr('no'), false],
[tr('yes'), true],
],
[DialogType.OkCancel]: [
['Cancel', false],
['OK', true],
[tr('cancel'), false],
[tr('ok'), true],
],
};

View File

@@ -11,13 +11,15 @@
color="info"
@click="clickBtn"
>
{{ config.btnText ? config.btnText : 'Close' }}
{{ config.btnText ? config.btnText : $t('close') }}
</v-btn>
</v-snackbar>
</template>
<script>
import { useMutations, useState } from '@/store';
import { tr } from '@/locale';
import { timeout } from '@/utils';
export default {
@@ -45,6 +47,7 @@ export default {
}
return {
tr,
config,
changed,
clickBtn,

View File

@@ -2,7 +2,7 @@
<v-dialog v-model="value" persistent width="25em">
<v-card>
<v-toolbar dark color="primary">
<v-toolbar-title>Login</v-toolbar-title>
<v-toolbar-title>{{ $t('login') }}</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form
@@ -15,8 +15,8 @@
<v-text-field
v-model="params.username"
prepend-icon="mdi-account"
label="Username"
:rules="[v => !!v || 'Username is required']"
:label="$t('username')"
:rules="[v => !!v || $t('msg.item_is_required', { item: $t('username') })]"
autofocus
required
/>
@@ -25,9 +25,9 @@
prepend-icon="mdi-lock"
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
@click:append="showPassword = !showPassword"
label="Password"
:label="$t('password')"
:type="showPassword ? 'text' : 'password'"
:rules="[v => !!v || 'Password is required']"
:rules="[v => !!v || $t('msg.item_is_required', { item: $t('password') })]"
required
/>
</div>
@@ -46,7 +46,7 @@
:disabled="!valid || submitting"
:loading="submitting"
>
Submit
{{ $t('submit') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -55,6 +55,8 @@
<script lang="ts">
import Vue from 'vue';
import { tr } from '@/locale';
import api from '../Api';
export default Vue.extend({
@@ -63,6 +65,7 @@ export default Vue.extend({
},
data() {
return {
tr,
valid: false,
submitting: false,
showPassword: false,

View File

@@ -10,31 +10,31 @@
height="40px"
class="elevation-2"
>
<v-btn icon @click="confirmDelete" title="Delete" :disabled="!hasSelected">
<v-btn icon @click="confirmDelete" :title="$t('delete')" :disabled="!hasSelected">
<v-icon>mdi-delete</v-icon>
</v-btn>
<v-divider vertical inset />
<v-btn icon @click="resumeTorrents" title="Resume" :disabled="!hasSelected">
<v-btn icon @click="resumeTorrents" :title="$t('resume')" :disabled="!hasSelected">
<v-icon>mdi-play</v-icon>
</v-btn>
<v-btn icon @click="pauseTorrents" title="Pause" :disabled="!hasSelected">
<v-btn icon @click="pauseTorrents" :title="$t('pause')" :disabled="!hasSelected">
<v-icon>mdi-pause</v-icon>
</v-btn>
<v-divider vertical inset />
<v-btn icon @click="showInfo()" title="Info"
<v-btn icon @click="showInfo()" :title="$t('info')"
:disabled="!hasSelected || selectedRows.length > 5"
>
<v-icon>mdi-alert-circle</v-icon>
</v-btn>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" title="Category" :disabled="!hasSelected">
<v-btn icon v-on="on" :title="$t('category')" :disabled="!hasSelected">
<v-icon>mdi-folder</v-icon>
</v-btn>
</template>
<v-list class="category-actions">
<v-subheader @click.stop="">
Set category
{{ $t('title.set_category') }}
</v-subheader>
<v-list-item
v-for="(item, i) in allCategories"
@@ -57,7 +57,7 @@
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>
Reset
{{ $t('reset') }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
@@ -65,13 +65,13 @@
</v-menu>
<template v-if="!$vuetify.breakpoint.xsOnly">
<v-divider vertical inset />
<v-btn icon @click="reannounceTorrents" title="Reannounce">
<v-btn icon @click="reannounceTorrents" :title="$t('reannounce')">
<v-icon>mdi-bullhorn</v-icon>
</v-btn>
<v-btn icon @click="editTracker" title="Edit tracker">
<v-btn icon @click="editTracker" :title="$t('title.edit_tracker')">
<v-icon>mdi-server</v-icon>
</v-btn>
<v-btn icon @click="recheckTorrents" title="Recheck" :disabled="selectedRows.length == 0">
<v-btn icon @click="recheckTorrents" :title="$t('recheck')" :disabled="selectedRows.length == 0">
<v-icon>mdi-backup-restore</v-icon>
</v-btn>
</template>
@@ -159,6 +159,8 @@
import Vue from 'vue';
import { mapState, mapGetters, mapMutations } from 'vuex';
import _ from 'lodash';
import { tr } from '@/locale';
import ConfirmDeleteDialog from './dialogs/ConfirmDeleteDialog.vue';
import ConfirmSetCategoryDialog from './dialogs/ConfirmSetCategoryDialog.vue';
import EditTrackerDialog from './dialogs/EditTrackerDialog.vue';
@@ -256,17 +258,17 @@ export default Vue.extend({
data() {
const headers = [
{ text: 'Name', value: 'name' },
{ text: 'Size', value: 'size' },
{ text: 'Progress', value: 'progress' },
{ text: 'Status', value: 'state' },
{ text: 'Seeds', value: 'num_complete' },
{ text: 'Peers', value: 'num_incomplete' },
{ text: 'DL Speed', value: 'dlspeed' },
{ text: 'UP Speed', value: 'upspeed' },
{ text: 'ETA', value: 'eta' },
{ text: 'Ratio', value: 'ratio' },
{ text: 'Added', value: 'added_on' },
{ text: tr('name'), value: 'name' },
{ text: tr('size'), value: 'size' },
{ text: tr('progress'), value: 'progress' },
{ text: tr('status'), value: 'state' },
{ text: tr('seeds'), value: 'num_complete' },
{ text: tr('peers'), value: 'num_incomplete' },
{ text: tr('dl_speed'), value: 'dlspeed' },
{ text: tr('up_speed'), value: 'upspeed' },
{ text: tr('eta'), value: 'eta' },
{ text: tr('ratio'), value: 'ratio' },
{ text: tr('added_on'), value: 'added_on' },
];
const footerProps = {
@@ -274,6 +276,7 @@ export default Vue.extend({
};
return {
tr,
headers,
selectedRows: [],
toDelete: [],

View File

@@ -5,10 +5,10 @@
class="headline grey lighten-4"
>
<v-icon class="mr-2">mdi-delete</v-icon>
<span>Delete torrents</span>
<span>{{ $t('title.delete_torrents') }}</span>
</v-card-title>
<v-card-text class="pb-0">
Are you sure to delete selected torrents from transfer list?
{{ $t('dialog.delete_torrents.msg') }}
<ol class="torrents pt-6">
<li v-for="(row, i) in torrents" :key="i">
{{ row.name }}
@@ -18,26 +18,26 @@
<v-checkbox
v-model="deleteFiles"
prepend-icon="mdi-file-cancel"
label="Also delete files"
:label="$t('label.also_delete_files')"
/>
<v-checkbox
v-if="sameNamedTorrents.length > 0"
v-model="deleteSameNamed"
prepend-icon="mdi-file-multiple"
class="mt-0"
:label="`Also delete ${sameNamedTorrents.length} same named torrents`"
:label="$t('dialog.delete_torrents', sameNamedTorrents.length)"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="closeDialog">Cancel</v-btn>
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
<v-btn
@click="submit"
color="warning"
:disabled="submitting"
:loading="submitting"
>
Delete
{{ $t('delete') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -48,6 +48,8 @@
import _ from 'lodash';
import Vue, { PropType } from 'vue';
import { mapGetters } from 'vuex';
import { tr } from '@/locale';
import api from '@/Api';
import { findSameNamedTorrents } from '@/utils';
import { Torrent } from '../../types';
@@ -58,6 +60,7 @@ export default Vue.extend({
},
data() {
return {
tr,
deleteFiles: false,
deleteSameNamed: false,
submitting: false,

View File

@@ -5,14 +5,14 @@
class="headline grey lighten-4"
>
<v-icon class="mr-2">mdi-folder</v-icon>
<span>Set category</span>
<span>{{ $t('title.set_category') }}</span>
</v-card-title>
<v-card-text class="pb-0">
<template v-if="category">
Are you sure to move selected torrents to category {{ category }}?
{{ $t('dialog.set_category.move', { category }) }}
</template>
<template v-else>
Are you sure to reset category of selected torrents?
{{ $t('dialog.set_category.reset') }}
</template>
<ol class="torrents pt-6">
<li v-for="(row, i) in torrents" :key="i">
@@ -25,19 +25,19 @@
v-model="moveSameNamed"
prepend-icon="mdi-file-multiple"
class="mt-0"
:label="`Also move ${sameNamedTorrents.length} same named torrents`"
:label="$t('dialog.set_category.also.move_same_name_torrents', sameNamedTorrents.length)"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="closeDialog">Cancel</v-btn>
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
<v-btn
@click="submit"
color="warning"
:disabled="submitting"
:loading="submitting"
>
Submit
{{ $t('submit') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -48,6 +48,8 @@
import _ from 'lodash';
import Vue from 'vue';
import { mapGetters } from 'vuex';
import { tr } from '@/locale';
import api from '@/Api';
import { findSameNamedTorrents } from '@/utils';