updated vuetify & fucked everything up

This commit is contained in:
Daan Wijns 2020-05-20 13:57:11 +02:00
parent 57e22c88db
commit ebc026460b
18 changed files with 7293 additions and 4130 deletions

View file

@ -1,30 +1,25 @@
module.exports = { module.exports = {
root: true,
env: { env: {
browser: true, node: true
commonjs: true,
es6: true
},
extends: ['plugin:vue/essential', 'airbnb-base'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
}, },
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
parserOptions: { parserOptions: {
ecmaVersion: 2018 parser: 'babel-eslint'
}, },
plugins: ['vue', 'prettier'],
rules: { rules: {
semi: ['warn', 'never'], 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-console': 0, 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
camelcase: 0, },
'no-restricted-syntax': 0, overrides: [
'no-shadow': 0, {
'class-methods-use-this': 0, files: [
'prefer-promise-reject-errors': 0, '**/__tests__/*.{j,t}s?(x)',
'no-underscore-dangle': 0, '**/tests/unit/**/*.spec.{j,t}s?(x)'
'no-param-reassign': 0, ],
'no-unused-vars': 0, env: {
indent: 0, jest: true
'comma-dangle': 0
} }
} }
]
}

View file

@ -1,3 +1,3 @@
module.exports = { module.exports = {
presets: ['@vue/app'], presets: ['@vue/cli-plugin-babel/preset']
} }

3
jest.config.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = {
preset: '@vue/cli-plugin-unit-jest'
}

10602
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,69 +9,44 @@
"format": "pretty-quick" "format": "pretty-quick"
}, },
"dependencies": { "dependencies": {
"@babel/polyfill": "^7.4.4",
"core-js": "^3.6.4",
"apexcharts": "^3.5.0", "apexcharts": "^3.5.0",
"axios": "^0.18.1", "axios": "^0.18.1",
"cors": "^2.8.5",
"date-fns": "^1.30.1", "date-fns": "^1.30.1",
"dotenv": "^8.2.0",
"eslint-plugin-prettier": "^3.1.3", "eslint-plugin-prettier": "^3.1.3",
"express": "^4.17.1",
"filepond": "^4.13.6",
"filepond-plugin-file-validate-size": "^2.2.1",
"filepond-plugin-file-validate-type": "^1.2.5",
"filepond-plugin-image-preview": "^4.6.4",
"lodash": "^4.17.15",
"multer": "^1.4.2", "multer": "^1.4.2",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"qbittorrent-api-v2": "^1.2.0",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-apexcharts": "^1.3.0", "vue-apexcharts": "^1.3.0",
"vue-filepond": "^5.1.3", "vue-context": "^5.1.0",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
"vue-toastification": "^1.7.1", "vue-toastification": "^1.7.1",
"vuetify": "^1.5.24", "vuetify": "^2.2.11",
"vuex": "^3.4.0" "vuex": "^3.4.0",
"vuex-persist": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.12.1", "@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "^3.12.1", "@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-plugin-pwa": "^3.12.1", "@vue/cli-plugin-pwa": "~4.3.0",
"@vue/cli-service": "^3.12.1", "@vue/cli-plugin-router": "~4.3.0",
"eslint": "^6.8.0", "@vue/cli-plugin-unit-jest": "~4.3.0",
"eslint-config-airbnb-base": "^14.1.0", "@vue/cli-plugin-vuex": "~4.3.0",
"eslint-plugin-import": "^2.20.2", "@vue/cli-service": "~4.3.0",
"eslint-plugin-vue": "^5.2.3", "@vue/eslint-config-prettier": "^6.0.0",
"stylus": "^0.54.7", "@vue/test-utils": "1.0.0-beta.31",
"stylus-loader": "^3.0.1", "babel-eslint": "^10.1.0",
"vue-cli-plugin-vuetify": "^0.4.6", "eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.2.2",
"laravel-mix": "^5.0.4",
"prettier": "^1.19.1",
"pretty-quick": "^2.0.1",
"sass-loader": "^8.0.2",
"tailwindcss": "^1.4.2",
"vue-cli-plugin-vuetify": "~2.0.5",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.4.3", "vuetify-loader": "^1.4.3"
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
} }
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<v-app class="grey lighten-4"> <v-app :style="{ background: $vuetify.theme.themes[theme].background }">
<div v-if="authenticated"> <div v-if="authenticated">
<Navbar /> <Navbar />
<v-content class="mx-4 mb-4"> <v-content class="mx-4 mb-4">
@ -29,10 +29,9 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState, mapGetters } from 'vuex'
import Navbar from './components/Navbar.vue' import Navbar from './components/Navbar.vue'
import Login from './components/Login.vue' import Login from './components/Login.vue'
import qbit from './services/qbit'
export default { export default {
components: { Navbar, Login }, components: { Navbar, Login },
@ -41,7 +40,11 @@ export default {
return {} return {}
}, },
computed: { computed: {
...mapState(['authenticated', 'rid', 'mainData', 'preferences']) ...mapState(['authenticated', 'rid', 'mainData', 'preferences']),
...mapGetters(['getTheme']),
theme() {
return this.getTheme() ? 'dark' : 'light'
}
} }
} }
</script> </script>

View file

@ -1,116 +0,0 @@
<template>
<v-dialog max-width="400px" v-model="dialog">
<v-btn flat small fab slot="activator" color="grey" class="mr-0 ml-0">
<v-icon color="grey">add</v-icon>
</v-btn>
<v-card>
<v-container :class="`pa-0 project done`">
<v-card-title class="justify-center">
<h2>Add a new Torrent</h2>
</v-card-title>
<div class="mr-5 ml-5">
<file-pond
name="file"
ref="pond"
label-idle="Drop file here..."
accepted-file-types="application/x-bittorrent"
data-max-file-size="1MB"
server="/upload"
v-model="Files"
/>
</div>
<v-card-text>
<v-form class="px-3" ref="form">
<!-- <v-text-field
v-model="filename"
label="File"
prepend-icon="attach_file"
:rules="inputRules"
></v-text-field>-->
<v-text-field v-model="directory" label="Download Directory" prepend-icon="folder"></v-text-field>
<v-spacer></v-spacer>
<v-card-actions class="justify-center">
<v-btn
:loading="loading"
flat
@click="submit"
class="blue_accent white--text mx-0 mt-3"
>Add Torrent</v-btn>
</v-card-actions>
</v-form>
</v-card-text>
</v-container>
</v-card>
</v-dialog>
</template>
<script>
// Import Vue FilePond
import vueFilePond from 'vue-filepond'
// Import FilePond styles
import 'filepond/dist/filepond.min.css'
// Import image preview plugin styles
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
// Import image preview and file type validation plugins
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
import FilePondPluginFileValidateSize from 'filepond-plugin-file-validate-size'
// Create component
const FilePond = vueFilePond(
FilePondPluginFileValidateType,
FilePondPluginImagePreview,
FilePondPluginFileValidateSize,
)
export default {
data() {
return {
filename: '',
directory: '',
inputRules: [
(v) => v.indexOf('magnet') > -1
|| v.indexOf('http') > -1
|| this.validFile
|| 'Not a valid magnet link',
],
loading: false,
dialog: false,
Files: [],
}
},
methods: {
submit() {
if (this.$refs.form.validate()) {
this.loading = true
this.$store.dispatch('ADD_TORRENT', {
name: this.filename,
dir: this.directory,
})
// reset input
this.$refs.form.reset()
this.filename = ''
this.directory = ''
this.$refs.pond.removeFiles()
this.dialog = false
this.loading = false
}
},
},
computed: {
validFile() {
return this.Files.length > 0
},
},
components: {
vueFilePond,
},
}
</script>

View file

@ -0,0 +1,108 @@
<template>
<v-dialog max-width="400px" v-model="dialog">
<v-card>
<v-container :class="`pa-0 project done`">
<v-card-title class="justify-center">
<h2>Add a new Torrent</h2>
</v-card-title>
<v-container>
<div ref="fileZone">
<v-file-input
v-show="files.length"
v-model="files"
ref="file"
multiple
chips
outlined
label="files"
/>
<v-textarea
v-show="!files.length"
label="URL"
placeholder="add Torrent"
prepend-icon="mdi-link"
append-outer-icon="mdi-attachment"
:rules="[v => !!files.length || !!v]"
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
required
:autofocus="!phoneLayout"
:value="params.urls"
@input="setParams('urls', $event)"
/>
</div>
</v-container>
<v-card-text>
<v-form class="px-3" ref="form">
<v-text-field
v-model="directory"
label="Download Directory"
prepend-icon="folder"
></v-text-field>
<v-spacer></v-spacer>
<v-card-actions class="justify-center">
<v-btn
:loading="loading"
flat
@click="submit"
class="blue_accent white--text mx-0 mt-3"
>Add Torrent</v-btn
>
</v-card-actions>
</v-form>
</v-card-text>
</v-container>
</v-card>
</v-dialog>
</template>
<script>
import Modal from '@/mixins/Modal'
export default {
name: 'AddModal',
mixins: [Modal],
data() {
return {
files: [],
directory: '',
inputRules: [
v =>
v.indexOf('magnet') > -1 ||
v.indexOf('http') > -1 ||
this.validFile ||
'Not a valid magnet link'
],
loading: false,
params: {}
}
},
methods: {
submit() {
if (this.$refs.form.validate()) {
this.loading = true
this.$store.dispatch('ADD_TORRENT', {
name: this.filename,
dir: this.directory
})
// reset input
this.$refs.form.reset()
this.filename = ''
this.directory = ''
this.$refs.pond.removeFiles()
this.dialog = false
this.loading = false
}
}
},
computed: {
validFile() {
return this.Files.length > 0
},
phoneLayout() {
return this.$vuetify.breakpoint.xsOnly
}
}
}
</script>

View file

@ -1,11 +1,11 @@
<template> <template>
<nav> <nav>
<!--title--> <!--title-->
<v-toolbar flat app> <v-app-bar flat>
<v-toolbar-side-icon <v-app-bar-nav-icon
@click="drawer = !drawer" @click="drawer = !drawer"
class="grey--text" class="grey--text text--darken-5"
></v-toolbar-side-icon> ></v-app-bar-nav-icon>
<v-toolbar-title <v-toolbar-title
:class="[ :class="[
'grey--text', 'grey--text',
@ -18,20 +18,29 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
<!--right corner functions--> <!--right corner functions-->
<AddTorrent @torrentAdded="snackbar = true" /> <v-btn
<v-btn small fab flat class="mr-0 ml-0" @click="removeTorrents"> text
small
fab
color="grey"
class="mr-0 ml-0"
@click="toggleModal('addmodal')"
>
<v-icon color="grey">add</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="removeTorrents">
<v-icon color="grey">remove</v-icon> <v-icon color="grey">remove</v-icon>
</v-btn> </v-btn>
<v-btn small fab flat class="mr-0 ml-0" @click="resumeTorrents"> <v-btn small fab text class="mr-0 ml-0" @click="resumeTorrents">
<v-icon color="grey">play_arrow</v-icon> <v-icon color="grey">play_arrow</v-icon>
</v-btn> </v-btn>
<v-btn small fab flat class="mr-0 ml-0" @click="pauseTorrents"> <v-btn small fab text class="mr-0 ml-0" @click="pauseTorrents">
<v-icon color="grey">pause</v-icon> <v-icon color="grey">pause</v-icon>
</v-btn> </v-btn>
<v-btn small fab flat class="mr-0 ml-0" @click="refreshTorrents"> <v-btn small fab text class="mr-0 ml-0" @click="refreshTorrents">
<v-icon color="grey">autorenew</v-icon> <v-icon color="grey">autorenew</v-icon>
</v-btn> </v-btn>
</v-toolbar> </v-app-bar>
<!--navigation drawer itself --> <!--navigation drawer itself -->
<v-navigation-drawer app v-model="drawer" class="primary allow-spacer"> <v-navigation-drawer app v-model="drawer" class="primary allow-spacer">
<!--current download speeds --> <!--current download speeds -->
@ -135,51 +144,17 @@
</v-layout> </v-layout>
</v-card> </v-card>
</v-flex> </v-flex>
<v-spacer></v-spacer>
<v-layout class="align-end">
<Settings />
<v-spacer></v-spacer>
<v-tooltip top v-if="paused">
<v-btn
small
fab
flat
class="mr-4"
@click="startInterval"
slot="activator"
>
<v-icon color="green_accent">play_arrow</v-icon>
</v-btn>
<span>Resumes connection to client</span>
</v-tooltip>
<v-tooltip top v-else>
<v-btn
small
fab
flat
class="mr-4"
@click="clearInterval"
slot="activator"
>
<v-icon color="green_accent">pause</v-icon>
</v-btn>
<span>Pauses connection to client</span>
</v-tooltip>
</v-layout>
</v-navigation-drawer> </v-navigation-drawer>
</nav> </nav>
</template> </template>
<script> <script>
import { mapMutations, mapGetters, mapState } from 'vuex' import { mapMutations, mapState } from 'vuex'
import { setInterval } from 'timers' import { setInterval } from 'timers'
import VueApexCharts from 'vue-apexcharts' import VueApexCharts from 'vue-apexcharts'
import ApexCharts from 'apexcharts'
import AddTorrent from './AddTorrent'
import Settings from './Settings'
export default { export default {
components: { AddTorrent, Settings, apexcharts: VueApexCharts }, components: { apexcharts: VueApexCharts },
data() { data() {
return { return {
drawer: false, drawer: false,
@ -244,6 +219,9 @@ export default {
refreshTorrents() {}, refreshTorrents() {},
updateChart() { updateChart() {
this.$refs.chart.updateSeries(this.series, true) this.$refs.chart.updateSeries(this.series, true)
},
toggleModal(name) {
this.$store.commit('TOGGLE_MODAL', name)
} }
}, },
computed: { computed: {

View file

@ -1,19 +0,0 @@
<template>
<v-dialog max-width="600px" max-height="800px">
<v-btn flat small fab slot="activator" color="grey" class="mr-0 ml-0">
<v-icon color="blue_accent">settings</v-icon>
</v-btn>
<v-container class="grey lighten-4">
<v-card class="pa-3">
<div>Nothing here yet!</div>
</v-card>
<div class="mt-3">
<v-card-actions class="justify-center">
<v-btn target="blank" href="https://paypal.me/Dwijns" class="blue_accent white--text">
<v-icon>attach_money</v-icon>support me on paypal (or dont)
</v-btn>
</v-card-actions>
</div>
</v-container>
</v-dialog>
</template>

View file

@ -1,19 +1,33 @@
import Vue from 'vue' import Vue from 'vue'
import './plugins/vuetify' import App from '@/App.vue'
import Toast from 'vue-toastification' import '@/registerServiceWorker'
import App from './App.vue' import router from '@/router'
import router from './router' import store from '@/store'
import store from './services/store' import vuetify from '@/plugins/vuetify'
import './registerServiceWorker' import '@babel/polyfill'
import Toast from 'vue-toastification'
import 'vue-toastification/dist/index.css' import 'vue-toastification/dist/index.css'
Vue.use(Toast) Vue.use(Toast)
Vue.config.productionTip = false Vue.config.productionTip = false
// register modals
const files = require.context('@/components/Modals', true, /\.vue$/i)
files.keys().map(key =>
Vue.component(
key
.split('/')
.pop()
.split('.')[0],
files(key).default
)
)
new Vue({ new Vue({
router, router,
store, store,
vuetify,
render: h => h(App) render: h => h(App)
}).$mount('#app') }).$mount('#app')

17
src/mixins/Modal.js Normal file
View file

@ -0,0 +1,17 @@
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['getAvailableTiles', 'getModalState']),
availableTiles() {
return this.getAvailableTiles()
},
dialog: {
get() {
return this.getModalState(this.$options.name)
},
set() {
this.$store.commit('TOGGLE_MODAL', this.$options.name)
}
}
}
}

View file

@ -17,5 +17,4 @@ export default class Stat {
const f = Math.floor(Math.log(a) / Math.log(c)) const f = Math.floor(Math.log(a) / Math.log(c))
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}` return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
} }
} }

View file

@ -1,10 +1,14 @@
import Vue from 'vue' import Vue from 'vue'
import Vuetify from 'vuetify/lib' import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl' import 'vuetify/dist/vuetify.min.css'
Vue.use(Vuetify, { Vue.use(Vuetify)
export default new Vuetify({
iconfont: 'md', iconfont: 'md',
theme: { theme: {
themes: {
light: {
primary: '#35495e', primary: '#35495e',
secondary: '#3e556d', secondary: '#3e556d',
secondary_lighter: '#56718c', secondary_lighter: '#56718c',
@ -14,5 +18,20 @@ Vue.use(Vuetify, {
green_accent: '#3cd1c2', green_accent: '#3cd1c2',
download: '#64CEAA', download: '#64CEAA',
upload: '#00b3fa', upload: '#00b3fa',
background: '#0000'
}, },
dark: {
primary: '#35495e',
secondary: '#3e556d',
secondary_lighter: '#56718c',
blue_accent: '#3cd1c2',
info: '#ffaa2c',
error: '#f83e70',
green_accent: '#3cd1c2',
download: '#64CEAA',
upload: '#00b3fa',
background: '#0000'
}
}
}
}) })

View file

@ -62,7 +62,7 @@ class Qbit {
} else { } else {
data = new URLSearchParams(params) data = new URLSearchParams(params)
} }
return this.axios.post('/torrents/add', data).then(Api.handleResponse) return this.axios.post('/torrents/add', data)
} }
switchToOldUi() { switchToOldUi() {
@ -90,9 +90,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios return this.axios.post('/torrents/filePrio', data)
.post('/torrents/filePrio', data)
.then(Api.handleResponse)
} }
getLogs(lastId) { getLogs(lastId) {
@ -100,11 +98,9 @@ class Qbit {
last_known_id: lastId last_known_id: lastId
} }
return this.axios return this.axios.get('/log/main', {
.get('/log/main', {
params params
}) })
.then(Api.handleResponse)
} }
toggleSpeedLimitsMode() { toggleSpeedLimitsMode() {
@ -140,11 +136,9 @@ class Qbit {
hash hash
} }
return this.axios return this.axios.get('/torrents/trackers', {
.get('/torrents/trackers', {
params params
}) })
.then(Api.handleResponse)
} }
getTorrentPeers(hash, rid) { getTorrentPeers(hash, rid) {
@ -153,11 +147,9 @@ class Qbit {
rid rid
} }
return this.axios return this.axios.get('/sync/torrentPeers', {
.get('/sync/torrentPeers', {
params params
}) })
.then(Api.handleResponse)
} }
editTracker(hash, origUrl, newUrl) { editTracker(hash, origUrl, newUrl) {
@ -173,11 +165,9 @@ class Qbit {
hash hash
} }
return this.axios return this.axios.get('/torrents/properties', {
.get('/torrents/properties', {
params params
}) })
.then(Api.handleResponse)
} }
getTorrentPieceStates(hash) { getTorrentPieceStates(hash) {
@ -185,11 +175,9 @@ class Qbit {
hash hash
} }
return this.axios return this.axios.get('/torrents/pieceStates', {
.get('/torrents/pieceStates', {
params params
}) })
.then(Api.handleResponse)
} }
getTorrentFiles(hash) { getTorrentFiles(hash) {
@ -197,11 +185,9 @@ class Qbit {
hash hash
} }
return this.axios return this.axios.get('/torrents/files', {
.get('/torrents/files', {
params params
}) })
.then(Api.handleResponse)
} }
getRssItems() { getRssItems() {
@ -209,11 +195,9 @@ class Qbit {
withData: true withData: true
} }
return this.axios return this.axios.get('/rss/items', {
.get('/rss/items', {
params params
}) })
.then(Api.handleResponse)
} }
addRssFeed(url, path = '') { addRssFeed(url, path = '') {
@ -223,7 +207,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.post('/rss/addFeed', data).then(Api.handleResponse) return this.axios.post('/rss/addFeed', data)
} }
removeRssFeed(path) { removeRssFeed(path) {
@ -232,7 +216,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.post('/rss/removeItem', data).then(Api.handleResponse) return this.axios.post('/rss/removeItem', data)
} }
refreshRssFeed(path) { refreshRssFeed(path) {
@ -241,9 +225,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios return this.axios.post('/rss/refreshItem', data)
.post('/rss/refreshItem', data)
.then(Api.handleResponse)
} }
moveRssFeed(path, newPath) { moveRssFeed(path, newPath) {
@ -253,11 +235,11 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.post('/rss/moveItem', data).then(Api.handleResponse) return this.axios.post('/rss/moveItem', data)
} }
getRssRules() { getRssRules() {
return this.axios.get('/rss/rules').then(Api.handleResponse) return this.axios.get('/rss/rules')
} }
setRssRule(name, def) { setRssRule(name, def) {
@ -267,7 +249,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.post('/rss/setRule', data).then(Api.handleResponse) return this.axios.post('/rss/setRule', data)
} }
removeRssRule(name) { removeRssRule(name) {
@ -276,7 +258,7 @@ class Qbit {
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.post('/rss/removeRule', data).then(Api.handleResponse) return this.axios.post('/rss/removeRule', data)
} }
actionTorrents(action, hashes, extra) { actionTorrents(action, hashes, extra) {
@ -285,9 +267,7 @@ class Qbit {
...extra ...extra
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios return this.axios.post(`/torrents/${action}`, data)
.post(`/torrents/${action}`, data)
.then(Api.handleResponse)
} }
} }

View file

@ -1,64 +0,0 @@
const axios = require('axios')
class Qbit {
constructor() {
this._axios = axios.create({
timeout: 1000
})
}
async getAll(sort) {
const res = await this._axios.post('/all', sort)
return res.data
}
async get_sessions_stats() {
const res = await this._axios.get('/session')
return res.data
}
async pause_torrents(torrents) {
const res = await this._axios.post('/pause', torrents)
return res.data
}
async pause_all() {
const res = await this._axios.post('/pause_all')
return res.data
}
async resume_torrents(torrents) {
const res = await this._axios.post('/resume', torrents)
return res.data
}
async resume_all() {
const res = await this._axios.post('/resume_all')
return res.data
}
async add_torrent(torrent) {
const res = await this._axios.post('/add', torrent)
return res
}
async remove_torrents(torrents) {
const res = await this._axios.post('/remove', torrents)
return res.data
}
async login(credentials) {
let timeout = false
const res = await this._axios
.post('/login', credentials)
.catch(error => {
if (error.code === 'ECONNABORTED') timeout = true
else throw error
})
return timeout ? 'timeout' : res.data
}
}
const qbit = new Qbit()
export default qbit

View file

@ -1,15 +1,22 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import { cloneDeep, merge, map, groupBy, sortBy } from 'lodash' import VuexPersist from 'vuex-persist'
import Torrent from '../models/torrent' import Torrent from '../models/torrent'
import Stat from '../models/sessionStat' import Stat from '../models/sessionStat'
import qbit from '../services/qbit'
import qbit from './qbit' const vuexPersist = new VuexPersist({
key: 'vuetorrent',
storage: window.localStorage
})
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
plugins: [vuexPersist.plugin],
state: { state: {
darkTheme: false,
intervals: [], intervals: [],
stats: null, stats: null,
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
@ -22,16 +29,29 @@ export default new Vuex.Store({
rid: 0, rid: 0,
mainData: undefined, mainData: undefined,
preferences: null, preferences: null,
pasteUrl: null pasteUrl: null,
modals: {
addmodal: false,
deletemodal: false,
settingsmodal: false
}
}, },
getters: { getters: {
CONTAINS_TORRENT: state => hash => CONTAINS_TORRENT: state => hash =>
state.selected_torrents.includes(hash) state.selected_torrents.includes(hash),
getTheme: state => () => state.darkTheme,
getModalState: state => name => state.modals[name.toLowerCase()]
}, },
mutations: { mutations: {
REMOVE_INTERVALS: state => { REMOVE_INTERVALS: state => {
state.intervals.forEach(el => clearInterval(el)) state.intervals.forEach(el => clearInterval(el))
}, },
TOGGLE_MODAL(state, modal) {
state.modals[modal.toLowerCase()] = !state.modals[
modal.toLowerCase()
]
},
ADD_SELECTED: (state, payload) => { ADD_SELECTED: (state, payload) => {
state.selected_torrents.push(payload) state.selected_torrents.push(payload)
}, },
@ -45,19 +65,17 @@ export default new Vuex.Store({
state.selected_torrents = [] state.selected_torrents = []
}, },
PAUSE_TORRENTS: async state => { PAUSE_TORRENTS: async state => {
let res
if (state.selected_torrents.length === 0) { if (state.selected_torrents.length === 0) {
res = await qbit.pause_all() qbit.pause_all()
} else { } else {
res = await qbit.pause_torrents(state.selected_torrents) qbit.pause_torrents(state.selected_torrents)
} }
}, },
RESUME_TORRENTS: async state => { RESUME_TORRENTS: async state => {
let res
if (state.selected_torrents.length === 0) { if (state.selected_torrents.length === 0) {
res = await qbit.resume_all() await qbit.resume_all()
} else { } else {
res = await qbit.resume_torrents(state.selected_torrents) await qbit.resume_torrents(state.selected_torrents)
} }
}, },
ADD_TORRENT: async (state, payload) => { ADD_TORRENT: async (state, payload) => {
@ -78,7 +96,7 @@ export default new Vuex.Store({
}, },
REMOVE_TORRENTS: async state => { REMOVE_TORRENTS: async state => {
if (state.selected_torrents.length !== 0) { if (state.selected_torrents.length !== 0) {
const res = await qbit.remove_torrents(state.selected_torrents) qbit.remove_torrents(state.selected_torrents)
} }
}, },
LOGIN: async (state, payload) => { LOGIN: async (state, payload) => {

View file

@ -126,7 +126,9 @@
<div class="right"> <div class="right">
<v-chip <v-chip
small small
:class="`${torrent.state} white--text my-2 caption`" :class="
`${torrent.state} white--text my-2 caption`
"
>{{ torrent.state }}</v-chip >{{ torrent.state }}</v-chip
> >
</div> </div>
@ -148,7 +150,7 @@
</template> </template>
<script> <script>
import { mapState, mapMutations, mapGetters } from 'vuex' import { mapState, mapMutations } from 'vuex'
export default { export default {
data() { data() {
@ -249,8 +251,12 @@ export default {
this.$store.state.sort_options = { name, reverse } this.$store.state.sort_options = { name, reverse }
}, },
selectTorrent(hash) {}, selectTorrent(hash) {
containsTorrent(hash) {}, return hash
},
containsTorrent(hash) {
return hash
},
resetSelected() {} resetSelected() {}
}, },
created() { created() {
@ -262,7 +268,7 @@ export default {
} }
</script> </script>
<style> <style scoped>
.project.done { .project.done {
border-left: 4px solid #3cd1c2; border-left: 4px solid #3cd1c2;
} }
@ -291,4 +297,5 @@ export default {
.pointer { .pointer {
cursor: pointer; cursor: pointer;
} }
</style> </style>