perf: sort dropdown in dashboard #309

This commit is contained in:
WDaan 2022-11-16 10:14:06 +01:00
parent 54b345682f
commit b557db48e8
8 changed files with 109 additions and 152 deletions

14
package-lock.json generated
View file

@ -28,7 +28,7 @@
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@mdi/js": "^5.9.55",
"@mdi/js": "^7",
"@types/jsdom": "^20.0.1",
"@typescript-eslint/eslint-plugin": "^5",
"@typescript-eslint/parser": "^5",
@ -1949,9 +1949,9 @@
}
},
"node_modules/@mdi/js": {
"version": "5.9.55",
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-5.9.55.tgz",
"integrity": "sha512-BbeHMgeK2/vjdJIRnx12wvQ6s8xAYfvMmEAVsUx9b+7GiQGQ9Za8jpwp17dMKr9CgKRvemlAM4S7S3QOtEbp4A==",
"version": "7.0.96",
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.0.96.tgz",
"integrity": "sha512-lNqhkV3cpPfYb/Avh+vXLFukUTbHbyHoFo4Jdc7Oc9UvURGVhamFIpgOVvEf2bNA78zvjXTZeVWExUTR+DLBfQ==",
"dev": true
},
"node_modules/@nodelib/fs.scandir": {
@ -9068,9 +9068,9 @@
}
},
"@mdi/js": {
"version": "5.9.55",
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-5.9.55.tgz",
"integrity": "sha512-BbeHMgeK2/vjdJIRnx12wvQ6s8xAYfvMmEAVsUx9b+7GiQGQ9Za8jpwp17dMKr9CgKRvemlAM4S7S3QOtEbp4A==",
"version": "7.0.96",
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.0.96.tgz",
"integrity": "sha512-lNqhkV3cpPfYb/Avh+vXLFukUTbHbyHoFo4Jdc7Oc9UvURGVhamFIpgOVvEf2bNA78zvjXTZeVWExUTR+DLBfQ==",
"dev": true
},
"@nodelib/fs.scandir": {

View file

@ -31,7 +31,7 @@
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@mdi/js": "^5.9.55",
"@mdi/js": "^7",
"@types/jsdom": "^20.0.1",
"@typescript-eslint/eslint-plugin": "^5",
"@typescript-eslint/parser": "^5",

View file

@ -5,12 +5,12 @@
</v-btn>
<v-dialog v-model="opened" width="50%">
<v-card class="pa-0">
<v-card-title class="justify-center pa-1" >
<v-card-title class="justify-center pa-1">
<v-toolbar flat dense class="transparent">
<v-toolbar-title>
<v-icon>{{ mdiToyBrick }}</v-icon> Plugin manager
<v-icon>{{ mdiToyBrick }}</v-icon> Plugin manager
</v-toolbar-title>
<v-spacer/>
<v-spacer />
<v-btn fab small class="transparent elevation-0" @click="close">
<v-icon>{{ mdiClose }}</v-icon>
</v-btn>
@ -54,7 +54,7 @@ export default {
togglePlugin(plugin) {
qbit.enableSearchPlugin([plugin.name], plugin.enabled)
},
close(){
close() {
this.opened = false
}
}

View file

@ -1,78 +0,0 @@
<template>
<v-dialog v-model="dialog" scrollable content-class="rounded-form" max-width="500px">
<v-card>
<v-card-title class="justify-center primarytext--text">
<h2>{{ $t('modals.sort.title') }}</h2>
</v-card-title>
<v-card-text>
<v-form class="px-6 mt-3 justify-center mx-auto">
<v-container class="sortmodal">
<v-select v-model="sort_options.sort" :value="sortProperty" flat class="ml-2 mr-2" :items="options" item-text="name" item-value="value" dense solo height="55" />
<v-switch v-model="sort_options.reverse" class="v-input--reverse v-input--expand pa-0 ma-0" inset color="accent" style="padding-left: 10px !important">
<template #label>
{{ $t('modals.sort.reverse') }}
</template>
</v-switch>
</v-container>
</v-form>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
import { mapState } from 'vuex'
import { Modal } from '@/mixins'
export default {
name: 'Sort',
mixins: [Modal],
data() {
return {
sortProperty: { value: 'added_on', name: 'Added On' },
reverse: true
}
},
computed: {
...mapState(['sort_options']),
options() {
return [
{ value: 'added_on', name: this.$i18n.t('modals.sort.sortBy.addedOn') },
{ value: 'availability', name: this.$i18n.t('modals.sort.sortBy.availability') },
{ value: 'category', name: this.$i18n.t('modals.sort.sortBy.category') },
{ value: 'completed', name: this.$i18n.t('modals.sort.sortBy.completed') },
{ value: 'dlspeed', name: this.$i18n.t('modals.sort.sortBy.downloadSpeed') },
{ value: 'downloaded', name: this.$i18n.t('modals.sort.sortBy.downloaded') },
{ value: 'eta', name: this.$i18n.t('modals.sort.sortBy.ETA') },
{ value: 'name', name: this.$i18n.t('modals.sort.sortBy.name') },
{ value: 'num_leechs', name: this.$i18n.t('modals.sort.sortBy.peers') },
{ value: 'priority', name: this.$i18n.t('modals.sort.sortBy.priority') },
{ value: 'progress', name: this.$i18n.t('modals.sort.sortBy.progress') },
{ value: 'ratio', name: this.$i18n.t('modals.sort.sortBy.ratio') },
{ value: 'save_path', name: this.$i18n.t('modals.sort.sortBy.save_path') },
{ value: 'size', name: this.$i18n.t('modals.sort.sortBy.size') },
{ value: 'state', name: this.$i18n.t('modals.sort.sortBy.state') },
{ value: 'time_active', name: this.$i18n.t('modals.sort.sortBy.timeActive') },
{ value: 'uploaded', name: this.$i18n.t('modals.sort.sortBy.uploaded') },
{ value: 'upspeed', name: this.$i18n.t('modals.sort.sortBy.uploadSpeed') }
]
}
},
methods: {
close() {
this.dialog = false
}
}
}
</script>
<style lang="scss" scoped>
.sortmodal .v-select__selection,
.v-input__icon i {
color: var(--search) !important;
}
// Reversed input variant
:deep(.v-input--reverse .v-input__slot) {
@import 'src/styles/styles.scss';
@include reverse-switch;
}
</style>

View file

@ -71,9 +71,9 @@ export default {
methods: {
create() {
this.$refs.categoryForm.validate()
return
qbit.createCategory(this.category)
this.cancel()
},

View file

@ -6,12 +6,12 @@
<v-subheader>
{{ $t('modals.settings.pageConnection.listeningSubHeader') }}
</v-subheader>
<v-list-item>
<v-text-field v-model="settings.listen_port" class="mb-2" outlined dense type="number" hide-details :label="$t('modals.settings.pageConnection.incomingConnectionPort')" />
</v-list-item>
<v-list-item>
<v-checkbox v-model="settings.upnp" hide-details class="ma-0 pa-0" :label="$t('modals.settings.pageConnection.useUPnP')" />
</v-list-item>
<v-list-item>
<v-text-field v-model="settings.listen_port" class="mb-2" outlined dense type="number" hide-details :label="$t('modals.settings.pageConnection.incomingConnectionPort')" />
</v-list-item>
<v-list-item>
<v-checkbox v-model="settings.upnp" hide-details class="ma-0 pa-0" :label="$t('modals.settings.pageConnection.useUPnP')" />
</v-list-item>
<v-subheader>
{{ $t('modals.settings.pageConnection.subHeader') }}
</v-subheader>
@ -133,7 +133,7 @@ export default {
bittorrent_protocol: [
{ value: 0, text: 'TCP and μTP' },
{ value: 1, text: 'TCP' },
{ value: 2, text: 'μTP' },
{ value: 2, text: 'μTP' }
]
}
},

View file

@ -10,7 +10,7 @@ export function formatBytes(a, b) {
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
}
import { mdiLanguageHtml5, mdiFileDocumentOutline, mdiNodejs, mdiFilePdf, mdiFileExcel, mdiCodeJson, mdiFileImage, mdiMovie, mdiLanguageMarkdown, mdiFile } from '@mdi/js'
import { mdiLanguageHtml5, mdiFileDocumentOutline, mdiNodejs, mdiFilePdfBox, mdiFileExcel, mdiCodeJson, mdiFileImage, mdiMovie, mdiLanguageMarkdown, mdiFile } from '@mdi/js'
export function getIconForFileType(type) {
const types = {
@ -18,7 +18,7 @@ export function getIconForFileType(type) {
js: mdiNodejs,
json: mdiCodeJson,
md: mdiLanguageMarkdown,
pdf: mdiFilePdf,
pdf: mdiFilePdfBox,
png: mdiFileImage,
txt: mdiFileDocumentOutline,
sub: mdiFileDocumentOutline,

View file

@ -1,13 +1,13 @@
<template>
<div class="px-1 px-sm-5 pt-4 background noselect" @click.self="resetSelected">
<v-row class="ma-0 pa-0" @click.self="resetSelected">
<v-row class="ma-0 pa-0 mb-2" @click.self="resetSelected">
<v-col v-if="topPagination && isMobile" cols="12" class="align-center justify-center pa-0">
<div class="text-center">
<v-pagination v-if="pageCount > 1 && !hasSearchFilter" v-model="pageNumber" :length="pageCount" :total-visible="7" @input="toTop" />
</div>
</v-col>
<v-expand-x-transition>
<v-card v-show="searchFilterEnabled" id="searchFilter" flat xs7 md3 class="ma-0 pa-0 mt-1 transparent">
<v-card v-show="searchFilterEnabled" id="searchFilter" flat xs7 md3 class="ma-0 pa-0 transparent">
<v-text-field
v-model="input"
autofocus
@ -25,48 +25,62 @@
/>
</v-card>
</v-expand-x-transition>
<v-row style="margin-top: 6px" class="mb-1 mx-1">
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Select Mode" v-on="on" @click="searchFilterEnabled = !searchFilterEnabled">
<v-icon color="grey">
{{ searchFilterEnabled ? mdiChevronLeftCircle : mdiTextBoxSearch }}
</v-icon>
</v-btn>
</template>
<span>Toggle Search Filter</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Select Mode" v-on="on" @click="toggleSelectMode()">
<v-icon color="grey">
{{ $store.state.selectMode ? mdiCheckboxMarked : mdiCheckboxBlankOutline }}
</v-icon>
</v-btn>
</template>
<span>Select Mode</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Sort Torrents" v-on="on" @click="addModal('SortModal')">
<v-icon color="grey">
{{ mdiSort }}
</v-icon>
</v-btn>
</template>
<span>Sort Torrents</span>
</v-tooltip>
<v-col v-if="topPagination && !isMobile" cols="8" class="align-center justify-center pa-0">
<div class="text-center">
<v-pagination v-if="pageCount > 1 && !hasSearchFilter" v-model="pageNumber" :length="pageCount" :total-visible="7" @input="toTop" />
</div>
</v-col>
<v-col class="align-center justify-center">
<span style="float: right; font-size: 0.8em" class="mr-2 text-uppercase">
{{ torrentCountString }}
</span>
</v-col>
</v-row>
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Select Mode" v-on="on" @click="searchFilterEnabled = !searchFilterEnabled">
<v-icon color="grey">
{{ searchFilterEnabled ? mdiChevronLeftCircle : mdiTextBoxSearch }}
</v-icon>
</v-btn>
</template>
<span>Toggle Search Filter</span>
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Select Mode" v-on="on" @click="toggleSelectMode">
<v-icon color="grey">
{{ $store.state.selectMode ? mdiCheckboxMarked : mdiCheckboxBlankOutline }}
</v-icon>
</v-btn>
</template>
<span>Select Mode</span>
</v-tooltip>
<v-expand-x-transition>
<v-card v-show="sortEnabled" flat class="ma-0 pa-0 mt-1 transparent">
<v-select
v-model="sort_options.sort"
flat
solo
dense
height="30"
class="ml-2 mr-2"
:items="sortOptions"
style="max-width: 10em"
:prepend-icon="sort_options.reverse ? mdiArrowUpThin : mdiArrowDownThin"
@click:prepend="$store.state.sort_options.reverse = !$store.state.sort_options.reverse"
/>
</v-card>
</v-expand-x-transition>
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn text small fab class="mr-0 ml-0" aria-label="Sort Torrents" v-on="on" @click="sortEnabled = !sortEnabled">
<v-icon color="grey">
{{ sortEnabled ? mdiChevronLeftCircle : mdiSort }}
</v-icon>
</v-btn>
</template>
<span>Sort Torrents</span>
</v-tooltip>
<v-col v-if="topPagination && !isMobile" cols="8" class="align-center justify-center pa-0">
<div class="text-center">
<v-pagination v-if="pageCount > 1 && !hasSearchFilter" v-model="pageNumber" :length="pageCount" :total-visible="7" @input="toTop" />
</div>
</v-col>
<v-col class="align-center justify-center">
<span style="float: right; font-size: 0.8em" class="mr-2 text-uppercase">
{{ torrentCountString }}
</span>
</v-col>
</v-row>
<v-row id="selectAllTorrents" class="ma-0 pa-0">
<v-expand-transition>
@ -129,8 +143,8 @@
<script>
import { mapState, mapGetters } from 'vuex'
import { mdiTextBoxSearch, mdiChevronLeftCircle, mdiMagnify, mdiCheckboxMarked, mdiCheckboxBlankOutline, mdiSort } from '@mdi/js'
import { QuickScore } from "quick-score"
import { mdiTextBoxSearch, mdiChevronLeftCircle, mdiMagnify, mdiCheckboxMarked, mdiCheckboxBlankOutline, mdiSort, mdiArrowUpThin, mdiArrowDownThin } from '@mdi/js'
import { QuickScore } from 'quick-score'
import Torrent from '@/components/Torrent/Torrent.vue'
import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue'
@ -157,22 +171,43 @@ export default {
},
trcMoveTick: 0,
searchFilterEnabled: false,
sortEnabled: false,
sortOptions: [
{ value: 'added_on', text: this.$i18n.t('modals.sort.sortBy.addedOn') },
{ value: 'availability', text: this.$i18n.t('modals.sort.sortBy.availability') },
{ value: 'category', text: this.$i18n.t('modals.sort.sortBy.category') },
{ value: 'completed', text: this.$i18n.t('modals.sort.sortBy.completed') },
{ value: 'dlspeed', text: this.$i18n.t('modals.sort.sortBy.downloadSpeed') },
{ value: 'downloaded', text: this.$i18n.t('modals.sort.sortBy.downloaded') },
{ value: 'eta', text: this.$i18n.t('modals.sort.sortBy.ETA') },
{ value: 'name', text: this.$i18n.t('modals.sort.sortBy.name') },
{ value: 'num_leechs', text: this.$i18n.t('modals.sort.sortBy.peers') },
{ value: 'priority', text: this.$i18n.t('modals.sort.sortBy.priority') },
{ value: 'progress', text: this.$i18n.t('modals.sort.sortBy.progress') },
{ value: 'ratio', text: this.$i18n.t('modals.sort.sortBy.ratio') },
{ value: 'save_path', text: this.$i18n.t('modals.sort.sortBy.save_path') },
{ value: 'size', text: this.$i18n.t('modals.sort.sortBy.size') },
{ value: 'state', text: this.$i18n.t('modals.sort.sortBy.state') },
{ value: 'time_active', text: this.$i18n.t('modals.sort.sortBy.timeActive') },
{ value: 'uploaded', text: this.$i18n.t('modals.sort.sortBy.uploaded') },
{ value: 'upspeed', text: this.$i18n.t('modals.sort.sortBy.uploadSpeed') }
],
mdiTextBoxSearch,
mdiChevronLeftCircle,
mdiMagnify,
mdiCheckboxBlankOutline,
mdiCheckboxMarked,
mdiSort
mdiSort,
mdiArrowUpThin,
mdiArrowDownThin
}
},
computed: {
...mapState(['mainData', 'selected_torrents', 'dashboard']),
...mapState(['mainData', 'selected_torrents', 'dashboard', 'sort_options']),
...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']),
torrents() {
if (!this.hasSearchFilter) return this.getTorrents()
// return this.getTorrents()
const qs = new QuickScore(this.getTorrents(), ['name', 'size', 'state', 'hash', 'savePath', 'tags', 'category'])
return qs.search(this.input).map(el => el.item)
},
@ -249,7 +284,7 @@ export default {
created() {
this.$store.dispatch('INIT_INTERVALS')
this.$store.commit('FETCH_CATEGORIES')
if(this.input) this.searchFilterEnabled = true
if (this.input) this.searchFilterEnabled = true
},
beforeDestroy() {
this.$store.commit('REMOVE_INTERVALS')