mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-02-18 00:02:02 +03:00
Feature categories (#35)
This commit is contained in:
parent
879cdc3420
commit
28e716ff29
38 changed files with 810 additions and 320 deletions
54
package-lock.json
generated
54
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vuetorrent",
|
||||
"version": "0.1.3",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -845,9 +845,9 @@
|
|||
}
|
||||
},
|
||||
"@babel/polyfill": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.4.tgz",
|
||||
"integrity": "sha512-8BYcnVqQ5kMD2HXoHInBH7H1b/uP3KdnwCYXOqFnXqguOyuu443WXusbIUbWEfY3Z0Txk0M1uG/8YuAMhNl6zg==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.11.5.tgz",
|
||||
"integrity": "sha512-FunXnE0Sgpd61pKSj2OSOs1D44rKTD3pGOfGilZ6LGrrIH0QEtJlTjqOqdF8Bs98JmjfGhni2BBkTfv9KcKJ9g==",
|
||||
"requires": {
|
||||
"core-js": "^2.6.5",
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
|
@ -1998,9 +1998,9 @@
|
|||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "3.20.0",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.20.0.tgz",
|
||||
"integrity": "sha512-DuQ9SlFPJBJwamYudzwf/Z6KMaIRUhnVBQk/TBa3mXzN2SwS3GgGz7V39OS1GfcPlPUTTy8vXv91M8pYmfFkCg==",
|
||||
"version": "3.20.1",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.20.1.tgz",
|
||||
"integrity": "sha512-86WedRPcIs45gdcVC+na0SDGIYcH378Z+TmOAyXYs4vwqjvbYyzA9VGFN2UorLgXHIV4RJm4kFJXdIBYh3aDiA==",
|
||||
"requires": {
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
|
@ -3909,9 +3909,9 @@
|
|||
}
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.8.34",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.34.tgz",
|
||||
"integrity": "sha512-Olb+E6EoMvdPmAMq2QoucuyZycKHjTlBXmRx8Ada+wGtq4SIXuDCdtoaX4KkK0yjf1fJLnwXQURr8gQKWKaybw=="
|
||||
"version": "1.8.35",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.35.tgz",
|
||||
"integrity": "sha512-isAbIEenO4ilm6f8cpqvgjZCsuerDAz2Kb7ri201AiNn58aqXuaLJEnCtfIMdCvERZHNGRY5lDMTr/jdAnKSWQ=="
|
||||
},
|
||||
"de-indent": {
|
||||
"version": "1.0.2",
|
||||
|
@ -6950,8 +6950,7 @@
|
|||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
|
@ -9748,6 +9747,14 @@
|
|||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
|
@ -10533,6 +10540,14 @@
|
|||
"faye-websocket": "^0.10.0",
|
||||
"uuid": "^3.4.0",
|
||||
"websocket-driver": "0.6.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"sockjs-client": {
|
||||
|
@ -11692,10 +11707,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
|
||||
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.0",
|
||||
|
@ -12529,6 +12543,14 @@
|
|||
"requires": {
|
||||
"ansi-colors": "^3.0.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-merge": {
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
"format": "pretty-quick"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.10.4",
|
||||
"apexcharts": "^3.19.3",
|
||||
"@babel/polyfill": "^7.11.5",
|
||||
"apexcharts": "^3.20.1",
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.6.4",
|
||||
"dayjs": "^1.8.29",
|
||||
"dayjs": "^1.8.35",
|
||||
"lodash": "^4.17.20",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"uuid": "^8.3.0",
|
||||
"vue": "^2.6.12",
|
||||
"vue-apexcharts": "^1.6.0",
|
||||
"vue-async-computed": "^3.9.0",
|
||||
|
|
20
src/App.vue
20
src/App.vue
|
@ -1,8 +1,11 @@
|
|||
<template>
|
||||
<v-app :style="{ backgroundColor: background }">
|
||||
<AddModal />
|
||||
<SettingsModal />
|
||||
<SearchModal />
|
||||
<component
|
||||
v-for="modal in modals"
|
||||
:key="modal.guid"
|
||||
:is="modal.component"
|
||||
v-bind="{ guid: modal.guid, ...modal.props }"
|
||||
/>
|
||||
<Navbar v-if="isAuthenticated" />
|
||||
<v-main fill-height fill-width>
|
||||
<router-view></router-view>
|
||||
|
@ -13,11 +16,10 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Navbar from '@/components/Navbar.vue'
|
||||
import SettingsModal from '@/components/Modals/SettingsModal/SettingsModal'
|
||||
import { isAuthenticated } from '@/services/auth.js'
|
||||
|
||||
export default {
|
||||
components: { Navbar, SettingsModal },
|
||||
components: { Navbar },
|
||||
name: 'App',
|
||||
data() {
|
||||
return {}
|
||||
|
@ -28,8 +30,12 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['rid', 'mainData', 'preferences']),
|
||||
...mapGetters(['getTheme']),
|
||||
...mapState(['rid', 'mainData', 'preferences', 'modals']),
|
||||
...mapGetters([
|
||||
'getTheme',
|
||||
'getDynamicComponent',
|
||||
'getDynamicComponent'
|
||||
]),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
|
|
|
@ -119,7 +119,7 @@ export default {
|
|||
|
||||
this.resetForm()
|
||||
|
||||
this.$store.commit('TOGGLE_MODAL', 'addmodal')
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
|
|
|
@ -164,7 +164,7 @@ export default {
|
|||
qbit.addTorrents(params)
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('TOGGLE_MODAL', 'SearchModal')
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<v-tab href="#bittorrent">BitTorrent</v-tab>
|
||||
<v-tab href="#webui">WebUI</v-tab>
|
||||
<v-tab href="#vuetorrent">VueTorrent</v-tab>
|
||||
<v-tab href="#tagsAndCategories">Tags & Categories</v-tab>
|
||||
</v-tabs>
|
||||
<perfect-scrollbar>
|
||||
<v-tabs-items
|
||||
|
@ -34,11 +35,16 @@
|
|||
<v-tab-item value="vuetorrent">
|
||||
<VueTorrent :is-active="tab === 'vuetorrent'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</perfect-scrollbar>
|
||||
</div>
|
||||
<v-card-actions class="d-flex justify-center">
|
||||
<v-btn color="success" @click="save_settings">Save</v-btn>
|
||||
<v-btn color="success" @click="saveSettings">Save</v-btn>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
@click="close"
|
||||
|
@ -62,12 +68,18 @@ import { mapGetters } from 'vuex'
|
|||
import qbit from '@/services/qbit'
|
||||
|
||||
import { Modal, FullScreenModal } from '@/mixins'
|
||||
import { WebUI, BitTorrent, Downloads, VueTorrent } from './Tabs'
|
||||
import {
|
||||
WebUI,
|
||||
BitTorrent,
|
||||
Downloads,
|
||||
VueTorrent,
|
||||
TagsAndCategories
|
||||
} from './Tabs'
|
||||
|
||||
export default {
|
||||
name: 'SettingsModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
components: { WebUI, BitTorrent, Downloads, VueTorrent },
|
||||
components: { WebUI, BitTorrent, Downloads, VueTorrent, TagsAndCategories },
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
|
@ -77,12 +89,14 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('TOGGLE_MODAL', 'SettingsModal')
|
||||
this.deleteModal()
|
||||
},
|
||||
save_settings() {
|
||||
saveSettings() {
|
||||
qbit.setPreferences(this.getSettings()).then(() => {
|
||||
Vue.$toast.success('Settings saved successfully!')
|
||||
})
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -93,11 +107,6 @@ export default {
|
|||
dialogHeight() {
|
||||
return this.phoneLayout ? '79vh' : '70vh'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialog(visible) {
|
||||
!visible ? (this.tab = null) : this.$store.commit('SET_SETTINGS')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
import SettingsTab from '@/mixins/SettingsTab'
|
||||
|
||||
export default {
|
||||
name: 'BitTorrent',
|
||||
name: 'Downloads',
|
||||
mixins: [SettingsTab]
|
||||
}
|
||||
</script>
|
||||
|
|
124
src/components/Modals/SettingsModal/Tabs/TagsAndCategories.vue
Normal file
124
src/components/Modals/SettingsModal/Tabs/TagsAndCategories.vue
Normal file
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<v-card-text
|
||||
class="mx-auto mt-5"
|
||||
style="font-size: 1.1em; max-height: 500px; min-height: 300px"
|
||||
>
|
||||
<v-layout class="mx-auto" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Tags:</h3>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 class="mt-3">
|
||||
<v-chip
|
||||
v-for="tag in availableTags"
|
||||
:key="tag"
|
||||
small
|
||||
class="download white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn text class="error white--text mt-3" @click="deleteTag"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createTag"
|
||||
>Create new</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<v-layout class="mx-auto" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Categories:</h3>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 class="mt-3">
|
||||
<v-chip
|
||||
v-for="cat in availableCategories"
|
||||
:key="cat.name"
|
||||
small
|
||||
class="upload white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="editCategory(cat)"
|
||||
@click:close="editCategory(cat)"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="deleteCategory"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createCategory"
|
||||
>Create new</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import qbit from '@/services/qbit'
|
||||
|
||||
import { Tab, General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'TagsAndCategories',
|
||||
mixins: [Tab, General],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
selectedCategory: null
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
},
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
activeMethod() {
|
||||
this.fetchCategories()
|
||||
},
|
||||
async fetchCategories() {
|
||||
const { data } = await qbit.getCategories()
|
||||
this.categories = data
|
||||
},
|
||||
deleteTag() {
|
||||
this.createModal('DeleteTagDialog')
|
||||
},
|
||||
createTag() {
|
||||
this.createModal('CreateTagDialog')
|
||||
},
|
||||
createCategory() {
|
||||
this.createModal('CreateCategoryDialog')
|
||||
},
|
||||
deleteCategory() {
|
||||
this.createModal('DeleteCategoryDialog')
|
||||
},
|
||||
editCategory(cat) {
|
||||
this.createModal('CreateCategoryDialog', { initialCategory: cat })
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,6 +1,7 @@
|
|||
import WebUI from '@/components/Modals/SettingsModal/Tabs/WebUI.vue'
|
||||
import BitTorrent from '@/components/Modals/SettingsModal/Tabs/BitTorrent.vue'
|
||||
import Downloads from '@/components/Modals/SettingsModal/Tabs/Downloads.vue'
|
||||
import VueTorrent from '@/components/Modals/SettingsModal/Tabs/VueTorrent.vue'
|
||||
import WebUI from './WebUI.vue'
|
||||
import BitTorrent from './BitTorrent.vue'
|
||||
import Downloads from './Downloads.vue'
|
||||
import VueTorrent from './VueTorrent.vue'
|
||||
import TagsAndCategories from './TagsAndCategories.vue'
|
||||
|
||||
export { WebUI, BitTorrent, Downloads, VueTorrent }
|
||||
export { WebUI, BitTorrent, Downloads, VueTorrent, TagsAndCategories }
|
||||
|
|
117
src/components/Modals/TagsAndCategories/CreateCategoryDialog.vue
Normal file
117
src/components/Modals/TagsAndCategories/CreateCategoryDialog.vue
Normal file
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Create New Category</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-form ref="categoryForm" class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
v-model="category.name"
|
||||
:rules="nameRules"
|
||||
:counter="15"
|
||||
label="Category name"
|
||||
required
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
v-model="category.savePath"
|
||||
:rules="PathRules"
|
||||
:counter="40"
|
||||
label="Path"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-if="!hasInitialCategory"
|
||||
text
|
||||
@click="create"
|
||||
class="green_accent white--text mt-3"
|
||||
>Save</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-else
|
||||
text
|
||||
@click="edit"
|
||||
class="green_accent white--text mt-3"
|
||||
>Edit</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
import Vue from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'createNewCategoryDialog',
|
||||
props: {
|
||||
initialCategory: Object
|
||||
},
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getSelectedCategory']),
|
||||
hasInitialCategory() {
|
||||
return (
|
||||
this.initialCategory &&
|
||||
this.initialCategory.name &&
|
||||
this.initialCategory.savePath
|
||||
)
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
nameRules: [
|
||||
v => !!v || 'Category name is required',
|
||||
v =>
|
||||
(v && v.length <= 15) ||
|
||||
'Category name must be less than 15 characters'
|
||||
],
|
||||
PathRules: [
|
||||
v => !!v || 'Path is required',
|
||||
v => (v && v.length <= 40) || 'Path must be less than 40 characters'
|
||||
],
|
||||
category: { name: '', savePath: '' }
|
||||
}),
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createCategory(this.category)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.category.name = ''
|
||||
this.category.savePath = ''
|
||||
this.$refs.categoryForm.reset()
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
},
|
||||
edit() {
|
||||
qbit.editCategory(this.category)
|
||||
Vue.$toast.success('Category edited successfully!')
|
||||
this.cancel()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
if (this.hasInitialCategory) {
|
||||
this.category = this.initialCategory
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -1,10 +1,7 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container
|
||||
style="min-height: 200px;"
|
||||
:class="`pa-0 project done`"
|
||||
>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Create New Tag</h2>
|
||||
</v-card-title>
|
||||
|
@ -13,7 +10,7 @@
|
|||
<v-container>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px;"
|
||||
style="max-width: 200px"
|
||||
v-model="tagname"
|
||||
:rules="rules"
|
||||
:counter="10"
|
||||
|
@ -40,11 +37,10 @@
|
|||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
export default {
|
||||
name: 'createNewTagDialog',
|
||||
props: {
|
||||
dialog: Boolean
|
||||
},
|
||||
name: 'createTagDialog',
|
||||
mixins: [Modal],
|
||||
data: () => ({
|
||||
tagname: '',
|
||||
rules: [
|
||||
|
@ -59,7 +55,7 @@ export default {
|
|||
},
|
||||
cancel() {
|
||||
this.tagname = ''
|
||||
this.$emit('close')
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<v-dialog :value="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Category</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-list
|
||||
rounded
|
||||
v-if="categories"
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
@click="deleteCategory(t)"
|
||||
v-for="(t, i) in categories"
|
||||
:key="i"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title
|
||||
v-text="t.name"
|
||||
></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
v-else
|
||||
>
|
||||
No categories found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Close</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'DeleteCategoryDialog',
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getCategories']),
|
||||
categories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteCategory(cat) {
|
||||
qbit.deleteCategory(cat.name)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -1,23 +1,20 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container
|
||||
style="min-height: 200px;"
|
||||
:class="`pa-0 project done`"
|
||||
>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Tag</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-list
|
||||
rounded
|
||||
v-if="tags"
|
||||
v-if="availableTags && availableTags.length"
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px;"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
@click="deleteTag(t)"
|
||||
v-for="(t, i) in tags"
|
||||
v-for="(t, i) in availableTags"
|
||||
:key="i"
|
||||
>
|
||||
<v-list-item-content>
|
||||
|
@ -25,6 +22,13 @@
|
|||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
v-else
|
||||
>
|
||||
No tags found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
|
@ -37,11 +41,16 @@
|
|||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'DeleteTagDialog',
|
||||
props: {
|
||||
dialog: Boolean,
|
||||
tags: Array
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags']),
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteTag(tag) {
|
||||
|
@ -49,8 +58,7 @@ export default {
|
|||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.tagname = ''
|
||||
this.$emit('close')
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
11
src/components/Modals/TagsAndCategories/index.js
Normal file
11
src/components/Modals/TagsAndCategories/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import CreateNewTagDialog from './CreateTagDialog.vue'
|
||||
import DeleteTagDialog from './DeleteTagDialog.vue'
|
||||
import CreateNewCategoryDialog from './CreateCategoryDialog.vue'
|
||||
import DeleteCategoryDialog from './DeleteCategoryDialog'
|
||||
|
||||
export {
|
||||
CreateNewTagDialog,
|
||||
DeleteTagDialog,
|
||||
CreateNewCategoryDialog,
|
||||
DeleteCategoryDialog
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text style="max-height: 500px; min-height: 400px;">
|
||||
<v-card-text style="max-height: 500px; min-height: 400px">
|
||||
<v-treeview
|
||||
v-model="tree"
|
||||
:items="fileTree"
|
||||
|
@ -68,5 +68,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-0" style="font-size: 1.1em;">
|
||||
<v-card-text class="pa-0" style="font-size: 1.1em">
|
||||
<v-simple-table>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -9,7 +9,7 @@
|
|||
{{ torrent.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="margin-top: 10px !important;">
|
||||
<tr style="margin-top: 10px !important">
|
||||
<td class="grey--text">hash</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.hash }}
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<v-card-text
|
||||
class="mx-auto mt-5"
|
||||
style="font-size: 1.1em; max-height: 500px; min-height: 300px"
|
||||
>
|
||||
<v-row>
|
||||
<v-col :cols="12" :lg="6" :md="6" :sm="12">
|
||||
<v-layout class="mx-auto" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Tags:</h3>
|
||||
</v-flex>
|
||||
<v-flex class="mt-3 d-flex justify-center" xs12 sm12>
|
||||
<v-chip
|
||||
v-for="tag in availableTags"
|
||||
:key="tag"
|
||||
small
|
||||
class="download white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="addTag(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout class="mx-auto mt-12" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Current Tags:</h3>
|
||||
</v-flex>
|
||||
<v-flex class="mt-3 d-flex justify-center" xs12 sm12>
|
||||
<div v-if="torrent.tags">
|
||||
<v-chip
|
||||
v-for="tag in torrent.tags"
|
||||
:key="tag"
|
||||
small
|
||||
close
|
||||
class="download white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="deleteTag(tag)"
|
||||
@click:close="deleteTag(tag)"
|
||||
>{{ tag }}</v-chip
|
||||
>
|
||||
</div>
|
||||
<div v-else>None</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
<v-col :cols="12" :lg="6" :md="6" :sm="12">
|
||||
<v-layout
|
||||
class="mx-auto"
|
||||
:class="
|
||||
this.$vuetify.breakpoint.smAndDown ? 'mt-12' : ''
|
||||
"
|
||||
row
|
||||
wrap
|
||||
>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Categories:</h3>
|
||||
</v-flex>
|
||||
<v-flex class="mt-3 d-flex justify-center" xs12 sm12>
|
||||
<v-chip
|
||||
v-for="cat in availableCategories"
|
||||
:key="cat.name"
|
||||
small
|
||||
class="upload white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="setCategory(cat.name)"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout class="mx-auto mt-12" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Current Category:</h3>
|
||||
</v-flex>
|
||||
<v-flex class="mt-3 d-flex justify-center" xs12 sm12>
|
||||
<v-chip
|
||||
v-if="torrent.category"
|
||||
small
|
||||
close
|
||||
class="upload white--text caption mx-2"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="deleteCategory"
|
||||
@click:close="deleteCategory"
|
||||
>{{ torrent.category }}</v-chip
|
||||
>
|
||||
<div v-else>None</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { difference } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import { Tab } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'TorrentTagsAndCategories',
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
mixins: [Tab],
|
||||
data: () => ({
|
||||
categories: []
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
},
|
||||
availableTags() {
|
||||
let availableTags = this.getAvailableTags()
|
||||
let currentTags = this.getTorrent(this.hash).tags
|
||||
return difference(availableTags, currentTags)
|
||||
},
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addTag(tag) {
|
||||
qbit.addTorrentTag(this.hash, tag)
|
||||
},
|
||||
deleteTag(tag) {
|
||||
qbit.removeTorrentTag(this.hash, tag)
|
||||
},
|
||||
setCategory(cat) {
|
||||
qbit.setCategory(this.hash, cat)
|
||||
},
|
||||
deleteCategory() {
|
||||
qbit.setCategory(this.hash, '')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,7 @@
|
|||
:headers="headers"
|
||||
:items="trackers"
|
||||
:hide-default-footer="true"
|
||||
style="max-height: 500px; min-height: 400px;"
|
||||
style="max-height: 500px; min-height: 400px"
|
||||
>
|
||||
<template v-slot:item="row">
|
||||
<tr>
|
7
src/components/Modals/TorrentDetailModal/Tabs/index.js
Normal file
7
src/components/Modals/TorrentDetailModal/Tabs/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Content from '@/components/Modals/TorrentDetailModal/Tabs/Content'
|
||||
import Info from '@/components/Modals/TorrentDetailModal/Tabs/Info'
|
||||
import Peers from '@/components/Modals/TorrentDetailModal/Tabs/Peers'
|
||||
import Trackers from '@/components/Modals/TorrentDetailModal/Tabs/Trackers'
|
||||
import TagsAndCategories from '@/components/Modals/TorrentDetailModal/Tabs/TorrentTagsAndCategories'
|
||||
|
||||
export { Content, Info, Peers, Trackers, TagsAndCategories }
|
|
@ -21,7 +21,7 @@
|
|||
<v-tab href="#trackers">Trackers</v-tab>
|
||||
<v-tab href="#peers">Peers</v-tab>
|
||||
<v-tab href="#content">Content</v-tab>
|
||||
<v-tab href="#tags">Tags</v-tab>
|
||||
<v-tab href="#tagsAndCategories">Tags & Categories</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab" touchless>
|
||||
<v-tab-item value="info">
|
||||
|
@ -39,8 +39,11 @@
|
|||
<v-tab-item value="content">
|
||||
<Content :is-active="tab === 'content'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tags">
|
||||
<Tags :is-active="tab === 'tags'" :hash="hash" />
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
:hash="hash"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
|
@ -57,12 +60,15 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
|
||||
import { Modal, FullScreenModal } from '@/mixins'
|
||||
import { Content, Info, Peers, Trackers, Tags } from './Tabs'
|
||||
import { Content, Info, Peers, Trackers, TagsAndCategories } from './Tabs'
|
||||
|
||||
export default {
|
||||
name: 'TorrentDetailModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
components: { Content, Info, Peers, Trackers, Tags },
|
||||
components: { Content, Info, Peers, Trackers, TagsAndCategories },
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
|
@ -72,14 +78,11 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('TOGGLE_MODAL', 'TorrentDetailModal')
|
||||
this.deleteModal()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
hash() {
|
||||
return this.$store.state.selectedDetailTorrent
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="toggleModal('searchmodal')"
|
||||
@click="addModal('SearchModal')"
|
||||
>
|
||||
<v-icon color="grey">search</v-icon>
|
||||
</v-btn>
|
||||
|
@ -36,7 +36,7 @@
|
|||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="toggleModal('addmodal')"
|
||||
@click="addModal('AddModal')"
|
||||
>
|
||||
<v-icon color="grey">add</v-icon>
|
||||
</v-btn>
|
||||
|
@ -54,7 +54,7 @@
|
|||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="toggleModal('settingsmodal')"
|
||||
@click="addModal('SettingsModal')"
|
||||
>
|
||||
<v-icon color="grey">settings</v-icon>
|
||||
</v-btn>
|
||||
|
@ -64,7 +64,7 @@
|
|||
app
|
||||
v-model="drawer"
|
||||
class="primary"
|
||||
style="position: fixed;"
|
||||
style="position: fixed"
|
||||
disable-resize-watcher
|
||||
>
|
||||
<!--current download speeds -->
|
||||
|
@ -82,36 +82,18 @@
|
|||
>
|
||||
<v-icon color="download">keyboard_arrow_down</v-icon>
|
||||
<span class="download--text title">
|
||||
{{
|
||||
status.dlspeed.substring(
|
||||
0,
|
||||
status.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.dlspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{
|
||||
status.dlspeed.substring(
|
||||
status.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.dlspeed | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
<v-icon class="pl-5" color="upload"
|
||||
>keyboard_arrow_up</v-icon
|
||||
>
|
||||
<span class="upload--text title">
|
||||
{{
|
||||
status.upspeed.substring(
|
||||
0,
|
||||
status.upspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.upspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{
|
||||
status.upspeed.substring(
|
||||
status.upspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.upspeed | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-layout>
|
||||
|
@ -139,7 +121,7 @@
|
|||
>
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px;"
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="download--text"
|
||||
>
|
||||
Downloaded
|
||||
|
@ -147,18 +129,9 @@
|
|||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="download--text title">
|
||||
{{
|
||||
status.downloaded.substring(
|
||||
0,
|
||||
status.downloaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.downloaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{
|
||||
status.downloaded.substring(
|
||||
status.downloaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.downloaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
|
@ -168,7 +141,7 @@
|
|||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px;"
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Uploaded
|
||||
|
@ -176,18 +149,9 @@
|
|||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{
|
||||
status.uploaded.substring(
|
||||
0,
|
||||
status.uploaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.uploaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{
|
||||
status.uploaded.substring(
|
||||
status.uploaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.uploaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
|
@ -197,14 +161,14 @@
|
|||
<v-card
|
||||
v-if="webuiSettings.showFreeSpace"
|
||||
flat
|
||||
style="margin-top: 30px;"
|
||||
style="margin-top: 30px"
|
||||
color="secondary"
|
||||
class="ml-2 mr-2"
|
||||
>
|
||||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px;"
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Free Space
|
||||
|
@ -212,18 +176,9 @@
|
|||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{
|
||||
status.freeDiskSpace.substring(
|
||||
0,
|
||||
status.freeDiskSpace.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.freeDiskSpace | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{
|
||||
status.freeDiskSpace.substring(
|
||||
status.freeDiskSpace.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
{{ status.freeDiskSpace | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
|
@ -293,9 +248,12 @@
|
|||
import { mapMutations, mapState, mapGetters } from 'vuex'
|
||||
import VueApexCharts from 'vue-apexcharts'
|
||||
import qbit from '@/services/qbit'
|
||||
import { General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'Navbar',
|
||||
components: { apexcharts: VueApexCharts },
|
||||
mixins: [General],
|
||||
data() {
|
||||
return {
|
||||
drawer: false,
|
||||
|
@ -331,7 +289,13 @@ export default {
|
|||
}
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'light'
|
||||
theme: 'light',
|
||||
x: {
|
||||
formatter: value => {
|
||||
let val = 32 - value * 2
|
||||
return val + ' seconds ago'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
chartInterval: null
|
||||
|
@ -351,8 +315,8 @@ export default {
|
|||
updateChart() {
|
||||
this.$refs.chart.updateSeries(this.series, true)
|
||||
},
|
||||
toggleModal(name) {
|
||||
this.$store.commit('TOGGLE_MODAL', name)
|
||||
addModal(name) {
|
||||
this.createModal(name)
|
||||
},
|
||||
toggleTheme() {
|
||||
this.$store.commit('TOGGLE_THEME')
|
||||
|
|
|
@ -23,36 +23,36 @@
|
|||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{ torrent.size | getNumber }}
|
||||
{{ torrent.size | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size | getUnit
|
||||
torrent.size | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<div>
|
||||
{{ torrent.dloaded | getNumber }}
|
||||
{{ torrent.dloaded | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dloaded | getUnit
|
||||
torrent.dloaded | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{ torrent.dlspeed | getNumber }}
|
||||
{{ torrent.dlspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed | getUnit
|
||||
torrent.dlspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{ torrent.upspeed | getNumber }}
|
||||
{{ torrent.upspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed | getUnit
|
||||
torrent.upspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
|
@ -90,11 +90,22 @@
|
|||
</div>
|
||||
</v-flex>
|
||||
<!-- labels -->
|
||||
<v-flex v-for="tag in torrent.tags" :key="tag" xs3 sm1 md1>
|
||||
<v-flex
|
||||
v-for="tag in torrent.tags.slice(0, 3)"
|
||||
:key="tag"
|
||||
xs3
|
||||
sm1
|
||||
md1
|
||||
>
|
||||
<v-chip small class="download white--text my-2 caption">
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<v-flex v-if="torrent.category" xs3 sm1 md1>
|
||||
<v-chip small class="upload white--text my-2 caption">
|
||||
{{ torrent.category }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 md12>
|
||||
<v-progress-linear
|
||||
height="3"
|
||||
|
@ -118,15 +129,30 @@
|
|||
<script>
|
||||
import { VueContext } from 'vue-context'
|
||||
import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue'
|
||||
import { General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'Torrent',
|
||||
components: {
|
||||
VueContext,
|
||||
TorrentRightClickMenu
|
||||
},
|
||||
mixins: [General],
|
||||
|
||||
props: {
|
||||
torrent: Object
|
||||
},
|
||||
computed: {
|
||||
chips() {
|
||||
let chips = []
|
||||
|
||||
if (this.torrent.category.length > 0) {
|
||||
chips.push(this.torrent.category)
|
||||
}
|
||||
|
||||
return chips
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectTorrent(hash) {
|
||||
if (this.containsTorrent(hash)) {
|
||||
|
@ -139,19 +165,10 @@ export default {
|
|||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
showInfo(hash) {
|
||||
this.$store.commit('TOGGLE_MODAL', 'TorrentDetailModal')
|
||||
this.$store.commit('SET_SELECTED_TORRENT_DETAIL', hash)
|
||||
this.createModal('TorrentDetailModal', { hash })
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
getUnit(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(value.indexOf(' '))
|
||||
},
|
||||
getNumber(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(0, value.indexOf(' '))
|
||||
},
|
||||
formatEta(value, options) {
|
||||
const minute = 60
|
||||
const hour = minute * 60
|
||||
|
|
|
@ -14,27 +14,27 @@
|
|||
<v-list dense rounded>
|
||||
<v-list-item @click="showInfo" link>
|
||||
<v-icon>info</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px;"
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Show Info</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="resume" link>
|
||||
<v-icon>play_arrow</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px;"
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Resume</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="pause" link>
|
||||
<v-icon>pause</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px;"
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Pause</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="reannounce" link>
|
||||
<v-icon>record_voice_over</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px;"
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>reannounce</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 15px; color: red;"
|
||||
style="font-size: 15px; color: red"
|
||||
>Delete</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 15px; color: red;"
|
||||
style="font-size: 15px; color: red"
|
||||
>Delete with files</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
@ -61,8 +61,10 @@
|
|||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { General } from '@/mixins'
|
||||
export default {
|
||||
name: 'TorrentRightClickMenu',
|
||||
mixins: [General],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
|
@ -83,8 +85,7 @@ export default {
|
|||
qbit.deleteTorrents([this.hash], true)
|
||||
},
|
||||
showInfo() {
|
||||
this.$store.commit('TOGGLE_MODAL', 'TorrentDetailModal')
|
||||
this.$store.commit('SET_SELECTED_TORRENT_DETAIL', this.hash)
|
||||
this.createModal('TorrentDetailModal', { hash: this.hash })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<v-card-text
|
||||
class="mx-auto mt-5"
|
||||
style="font-size: 1.1em; max-height: 500px; min-height: 300px;"
|
||||
>
|
||||
<v-layout class="mx-auto" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Tags:</h3>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 class="mt-3">
|
||||
<v-chip
|
||||
v-for="tag in availableTags"
|
||||
:key="tag"
|
||||
small
|
||||
class="download white--text caption mx-2"
|
||||
style="font-size: 0.95em !important;"
|
||||
@click="addTag(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout class="mx-auto mt-12" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Current Tags:</h3>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 class="mt-3">
|
||||
<v-chip
|
||||
v-for="tag in torrent.tags"
|
||||
:key="tag"
|
||||
small
|
||||
close
|
||||
class="download white--text caption mx-2"
|
||||
style="font-size: 0.95em !important;"
|
||||
@click="deleteTag(tag)"
|
||||
@click:close="deleteTag(tag)"
|
||||
>{{ tag }}</v-chip
|
||||
>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="DeleteDialog = true"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="CreateNewDialog = true"
|
||||
>Create new</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<CreateNewTagDialog
|
||||
:dialog="CreateNewDialog"
|
||||
@close="CreateNewDialog = false"
|
||||
/>
|
||||
<DeleteTagDialog
|
||||
:dialog="DeleteDialog"
|
||||
@close="DeleteDialog = false"
|
||||
:tags="availableTags"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { difference } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import CreateNewTagDialog from './CreateNewTagDialog'
|
||||
import DeleteTagDialog from './DeleteTagDialog'
|
||||
export default {
|
||||
name: 'Tags',
|
||||
components: { CreateNewTagDialog, DeleteTagDialog },
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
CreateNewDialog: false,
|
||||
DeleteDialog: false
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
},
|
||||
availableTags() {
|
||||
let availableTags = this.getAvailableTags()
|
||||
let currentTags = this.getTorrent(this.hash).tags
|
||||
return difference(availableTags, currentTags)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addTag(tag) {
|
||||
qbit.addTorrentTag(this.hash, tag)
|
||||
},
|
||||
deleteTag(tag) {
|
||||
qbit.removeTorrentTag(this.hash, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,7 +0,0 @@
|
|||
import Content from '@/components/TorrentDetailModal/Tabs/Content'
|
||||
import Info from '@/components/TorrentDetailModal/Tabs/Info'
|
||||
import Peers from '@/components/TorrentDetailModal/Tabs/Peers'
|
||||
import Trackers from '@/components/TorrentDetailModal/Tabs/Trackers'
|
||||
import Tags from '@/components/TorrentDetailModal/Tabs/Tags'
|
||||
|
||||
export { Content, Info, Peers, Trackers, Tags }
|
|
@ -80,3 +80,17 @@ export function networkSize(size) {
|
|||
}
|
||||
|
||||
Vue.filter('networkSize', networkSize)
|
||||
|
||||
function getDataUnit(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(value.indexOf(' '))
|
||||
}
|
||||
|
||||
Vue.filter('getDataUnit', getDataUnit)
|
||||
|
||||
function getDataValue(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(0, value.indexOf(' '))
|
||||
}
|
||||
|
||||
Vue.filter('getDataValue', getDataValue)
|
||||
|
|
14
src/mixins/General.js
Normal file
14
src/mixins/General.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { v4 as uuidv4 } from 'uuid'
|
||||
export default {
|
||||
methods: {
|
||||
createModal(name, props) {
|
||||
let component = {
|
||||
component: name,
|
||||
props,
|
||||
guid: uuidv4()
|
||||
}
|
||||
|
||||
this.$store.commit('ADD_MODAL', component)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,23 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
props: ['guid'],
|
||||
computed: {
|
||||
...mapGetters(['getModalState']),
|
||||
dialog: {
|
||||
get() {
|
||||
return this.getModalState(this.$options.name)
|
||||
return this.getModalState(this.guid)
|
||||
},
|
||||
set() {
|
||||
this.$store.commit('TOGGLE_MODAL', this.$options.name)
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteModal() {
|
||||
setTimeout(() => this.$store.commit('DELETE_MODAL', this.guid), 100)
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
|
|
13
src/mixins/Tab.js
Normal file
13
src/mixins/Tab.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
export default {
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.activeMethod()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import FullScreenModal from '@/mixins/FullScreenModal'
|
||||
import Modal from '@/mixins/Modal'
|
||||
import SettingsTab from '@/mixins/SettingsTab'
|
||||
import FullScreenModal from './FullScreenModal'
|
||||
import Modal from './Modal'
|
||||
import SettingsTab from './SettingsTab'
|
||||
import Tab from './Tab'
|
||||
import General from './General'
|
||||
|
||||
export { FullScreenModal, Modal, SettingsTab }
|
||||
export { FullScreenModal, Modal, SettingsTab, Tab, General }
|
||||
|
|
|
@ -21,6 +21,7 @@ export default class Torrent {
|
|||
this.progress = data.progress * 100
|
||||
this.ratio = Math.round(data.ratio * 100)
|
||||
this.tags = data.tags.length > 0 ? data.tags.split(',') : null
|
||||
this.category = data.category
|
||||
}
|
||||
|
||||
formatState(state) {
|
||||
|
|
|
@ -40,6 +40,14 @@ class Qbit {
|
|||
return this.axios.get('/app/preferences')
|
||||
}
|
||||
|
||||
setPreferences(params) {
|
||||
const data = new URLSearchParams({
|
||||
json: JSON.stringify(params)
|
||||
})
|
||||
|
||||
return this.axios.post('/app/setPreferences', data)
|
||||
}
|
||||
|
||||
getMainData(rid) {
|
||||
const params = {
|
||||
rid
|
||||
|
@ -78,14 +86,6 @@ class Qbit {
|
|||
return this.setPreferences(params)
|
||||
}
|
||||
|
||||
setPreferences(params) {
|
||||
const data = new URLSearchParams({
|
||||
json: JSON.stringify(params)
|
||||
})
|
||||
|
||||
return this.axios.post('/app/setPreferences', data)
|
||||
}
|
||||
|
||||
setTorrentFilePriority(hash, idList, priority) {
|
||||
const idListStr = idList.join('|')
|
||||
const params = {
|
||||
|
@ -253,6 +253,50 @@ class Qbit {
|
|||
return this.axios.post('/torrents/deleteTags ', data)
|
||||
}
|
||||
|
||||
// Begin Categories
|
||||
|
||||
getCategories() {
|
||||
return this.axios.get('/torrents/categories')
|
||||
}
|
||||
|
||||
deleteCategory(cat) {
|
||||
const params = {
|
||||
categories: cat
|
||||
}
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post('/torrents/removeCategories ', data)
|
||||
}
|
||||
|
||||
createCategory(cat) {
|
||||
const params = {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
}
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post('/torrents/createCategory ', data)
|
||||
}
|
||||
|
||||
setCategory(hash, cat) {
|
||||
const params = {
|
||||
hashes: hash,
|
||||
category: cat
|
||||
}
|
||||
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post('/torrents/setCategory ', data)
|
||||
}
|
||||
|
||||
editCategory(cat) {
|
||||
const params = {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
}
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post('/torrents/editCategory ', data)
|
||||
}
|
||||
|
||||
// End Categories
|
||||
|
||||
actionTorrents(action, hashes, extra) {
|
||||
const params = {
|
||||
hashes: hashes.join('|'),
|
||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
|||
Vue.$toast.success('Successfully logged in!')
|
||||
context.commit('LOGIN', true)
|
||||
context.commit('updateMainData')
|
||||
context.commit('SET_SETTINGS')
|
||||
context.commit('FETCH_SETTINGS')
|
||||
return true
|
||||
}
|
||||
Vue.$toast.error('Log in failed 😕')
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
export default {
|
||||
containsTorrent: state => hash => state.selected_torrents.includes(hash),
|
||||
getTheme: state => () => state.webuiSettings.darkTheme,
|
||||
getModalState: state => name => state.modals[name.toLowerCase()],
|
||||
getModalState: state => guid =>
|
||||
state.modals.filter(m => m.guid === guid)[0],
|
||||
getSettings: state => () => state.settings,
|
||||
getStatus: state => () => state.status,
|
||||
getTorrent: state => hash =>
|
||||
state.torrents.filter(el => el.hash === hash)[0],
|
||||
getWebuiSettings: state => () => state.webuiSettings,
|
||||
getAvailableTags: state => () => state.status.tags
|
||||
getAvailableTags: state => () => state.status.tags,
|
||||
getCategories: state => () => state.categories,
|
||||
getModals: state => () => state.modals
|
||||
}
|
||||
|
|
|
@ -31,19 +31,13 @@ export default new Vuex.Store({
|
|||
},
|
||||
rid: 0,
|
||||
pasteUrl: null,
|
||||
modals: {
|
||||
addmodal: false,
|
||||
deletemodal: false,
|
||||
settingsmodal: false,
|
||||
olduimodal: false,
|
||||
torrentdetailmodal: false
|
||||
},
|
||||
modals: [],
|
||||
settings: {},
|
||||
webuiSettings: {
|
||||
darkTheme: false,
|
||||
showFreeSpace: true
|
||||
},
|
||||
selectedDetailTorrent: null
|
||||
categories: []
|
||||
},
|
||||
getters: {
|
||||
...getters
|
||||
|
|
|
@ -6,8 +6,11 @@ export default {
|
|||
REMOVE_INTERVALS: state => {
|
||||
state.intervals.forEach(el => clearInterval(el))
|
||||
},
|
||||
TOGGLE_MODAL(state, modal) {
|
||||
state.modals[modal.toLowerCase()] = !state.modals[modal.toLowerCase()]
|
||||
ADD_MODAL(state, modal) {
|
||||
state.modals.push(modal)
|
||||
},
|
||||
DELETE_MODAL(state, guid) {
|
||||
state.modals = state.modals.filter(m => m.guid !== guid)
|
||||
},
|
||||
SET_SELECTED: (state, payload) => {
|
||||
if (payload.type === 'add') state.selected_torrents.push(payload.hash)
|
||||
|
@ -50,17 +53,18 @@ export default {
|
|||
state.torrents.push(new Torrent({ hash: key, ...value }))
|
||||
}
|
||||
},
|
||||
SET_SETTINGS: async state => {
|
||||
FETCH_SETTINGS: async state => {
|
||||
const { data } = await qbit.getAppPreferences()
|
||||
state.settings = data
|
||||
},
|
||||
SET_SELECTED_TORRENT_DETAIL: (state, hash) => {
|
||||
state.selectedDetailTorrent = hash
|
||||
},
|
||||
UPDATE_SORT_OPTIONS: (state, payload) => {
|
||||
state.sort_options.sort = payload.name
|
||||
state.sort_options.reverse = payload.reverse
|
||||
state.sort_options.hashes = payload.hashes ? payload.hashes : null
|
||||
state.sort_options.filter = payload.filter ? payload.filter : null
|
||||
},
|
||||
FETCH_CATEGORIES: async state => {
|
||||
const { data } = await qbit.getCategories()
|
||||
state.categories = data
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,20 +31,18 @@
|
|||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
<TorrentDetailModal />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import Torrent from '@/components/Torrent'
|
||||
import TorrentDetailModal from '@/components/TorrentDetailModal/TorrentDetailModal'
|
||||
|
||||
import { getPropName, sortOrFilter, filterOption } from '@/helpers'
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: { Torrent, TorrentDetailModal },
|
||||
components: { Torrent },
|
||||
data() {
|
||||
return {
|
||||
sort_input: ''
|
||||
|
|
Loading…
Add table
Reference in a new issue