mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
feat(filters): Allow to disable filters temporarily
This commit is contained in:
parent
55c2ef8115
commit
6f9ee5d0e3
8 changed files with 262 additions and 91 deletions
|
@ -12,7 +12,7 @@
|
|||
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<meta name="theme-color" content="#000" />
|
||||
<title>qBittorrent</title>
|
||||
<title>VueTorrent</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
|
|
@ -3,12 +3,19 @@ import { TorrentState } from '@/constants/qbit'
|
|||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { categories: _categories, tags: _tags, trackers: _trackers, filters } = storeToRefs(useMaindataStore())
|
||||
const { statusFilter, categoryFilter, tagFilter, trackerFilter } = toRefs(filters.value)
|
||||
const {
|
||||
categories: _categories,
|
||||
tags: _tags,
|
||||
trackers: _trackers,
|
||||
statusFilter,
|
||||
categoryFilter,
|
||||
tagFilter,
|
||||
trackerFilter
|
||||
} = storeToRefs(useMaindataStore())
|
||||
const vueTorrentStore = useVueTorrentStore()
|
||||
|
||||
const statuses = computed(() => Object.values(TorrentState).map(state => (
|
||||
|
|
|
@ -1,97 +1,225 @@
|
|||
<script setup lang="ts">
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useMaindataStore } from '@/stores/maindata.ts'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const dashboardStore = useDashboardStore()
|
||||
const { filters } = storeToRefs(useMaindataStore())
|
||||
const { statusFilter, categoryFilter, tagFilter, trackerFilter } = toRefs(filters.value)
|
||||
const {
|
||||
isTextFilterActive,
|
||||
textFilter,
|
||||
isStatusFilterActive,
|
||||
statusFilter,
|
||||
isCategoryFilterActive,
|
||||
categoryFilter,
|
||||
isTagFilterActive,
|
||||
tagFilter,
|
||||
isTrackerFilterActive,
|
||||
trackerFilter,
|
||||
} = storeToRefs(useMaindataStore())
|
||||
|
||||
const isTextFilterActive = computed(() => dashboardStore.searchFilter?.length > 0)
|
||||
const isStatusFilterActive = computed(() => statusFilter.value.length > 0)
|
||||
const isCategoryFilterActive = computed(() => categoryFilter.value.length > 0)
|
||||
const isTagFilterActive = computed(() => tagFilter.value.length > 0)
|
||||
const isTrackerFilterActive = computed(() => trackerFilter.value.length > 0)
|
||||
|
||||
const filterCount = computed(
|
||||
const globalFilterActive = computed(
|
||||
() =>
|
||||
Number(isTextFilterActive.value) +
|
||||
Number(isStatusFilterActive.value) +
|
||||
Number(isCategoryFilterActive.value) +
|
||||
Number(isTagFilterActive.value) +
|
||||
Number(isTrackerFilterActive.value)
|
||||
isTextFilterActive.value && isTextFilterPresent.value ||
|
||||
isStatusFilterActive.value && isStatusFilterPresent.value ||
|
||||
isCategoryFilterActive.value && isCategoryFilterPresent.value ||
|
||||
isTagFilterActive.value && isTagFilterPresent.value ||
|
||||
isTrackerFilterActive.value && isTrackerFilterPresent.value
|
||||
)
|
||||
|
||||
// TODO
|
||||
const isTextFilterPresent = computed(() => textFilter.value.length > 0)
|
||||
const isStatusFilterPresent = computed(() => statusFilter.value.length > 0)
|
||||
const isCategoryFilterPresent = computed(() => categoryFilter.value.length > 0)
|
||||
const isTagFilterPresent = computed(() => tagFilter.value.length > 0)
|
||||
const isTrackerFilterPresent = computed(() => trackerFilter.value.length > 0)
|
||||
|
||||
const globalFilterColor = computed(() => globalFilterActive.value ? 'active-global' : 'active-global-disabled')
|
||||
const textFilterColor = computed(() => isTextFilterActive.value ? 'active-text' : 'active-text-disabled')
|
||||
const singleStatusFilterColor = computed(() => isStatusFilterActive.value ? `torrent-${statusFilter.value[0]}` : `torrent-${statusFilter.value[0]}-darken-2`)
|
||||
const statusFilterColor = computed(() => isStatusFilterActive.value ? 'active-status' : 'active-status-disabled')
|
||||
const categoryFilterColor = computed(() => isCategoryFilterActive.value ? 'active-category' : 'active-category-disabled')
|
||||
const tagFilterColor = computed(() => isTagFilterActive.value ? 'active-tag' : 'active-tag-disabled')
|
||||
const trackerFilterColor = computed(() => isTrackerFilterActive.value ? 'active-tracker' : 'active-tracker-disabled')
|
||||
|
||||
const filterPresentCount = computed(
|
||||
() =>
|
||||
Number(isTextFilterPresent.value) +
|
||||
Number(isStatusFilterPresent.value) +
|
||||
Number(isCategoryFilterPresent.value) +
|
||||
Number(isTagFilterPresent.value) +
|
||||
Number(isTrackerFilterPresent.value)
|
||||
)
|
||||
|
||||
const filterActiveCount = computed(
|
||||
() =>
|
||||
Number(isTextFilterPresent.value && isTextFilterActive.value) +
|
||||
Number(isStatusFilterPresent.value && isStatusFilterActive.value) +
|
||||
Number(isCategoryFilterPresent.value && isCategoryFilterActive.value) +
|
||||
Number(isTagFilterPresent.value && isTagFilterActive.value) +
|
||||
Number(isTrackerFilterPresent.value && isTrackerFilterActive.value)
|
||||
)
|
||||
|
||||
function toggleAllFilters() {
|
||||
if (globalFilterActive.value) {
|
||||
isTextFilterActive.value = false
|
||||
isStatusFilterActive.value = false
|
||||
isCategoryFilterActive.value = false
|
||||
isTagFilterActive.value = false
|
||||
isTrackerFilterActive.value = false
|
||||
} else {
|
||||
isTextFilterActive.value = true
|
||||
isStatusFilterActive.value = true
|
||||
isCategoryFilterActive.value = true
|
||||
isTagFilterActive.value = true
|
||||
isTrackerFilterActive.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllFilters() {
|
||||
dashboardStore.searchFilter = ''
|
||||
statusFilter.value = []
|
||||
categoryFilter.value = []
|
||||
tagFilter.value = []
|
||||
trackerFilter.value = []
|
||||
resetTextFilter()
|
||||
resetStatusFilter()
|
||||
resetCategoryFilter()
|
||||
resetTagFilter()
|
||||
resetTrackerFilter()
|
||||
}
|
||||
|
||||
function toggleTextFilter() {
|
||||
isTextFilterActive.value = !isTextFilterActive.value
|
||||
}
|
||||
|
||||
function resetTextFilter() {
|
||||
dashboardStore.searchFilter = ''
|
||||
textFilter.value = ''
|
||||
}
|
||||
|
||||
function toggleStatusFilter() {
|
||||
isStatusFilterActive.value = !isStatusFilterActive.value
|
||||
}
|
||||
|
||||
function resetStatusFilter() {
|
||||
statusFilter.value = []
|
||||
}
|
||||
|
||||
function toggleCategoryFilter() {
|
||||
isCategoryFilterActive.value = !isCategoryFilterActive.value
|
||||
}
|
||||
|
||||
function resetCategoryFilter() {
|
||||
categoryFilter.value = []
|
||||
}
|
||||
|
||||
function toggleTagFilter() {
|
||||
isTagFilterActive.value = !isTagFilterActive.value
|
||||
}
|
||||
|
||||
function resetTagFilter() {
|
||||
tagFilter.value = []
|
||||
}
|
||||
|
||||
function toggleTrackerFilter() {
|
||||
isTrackerFilterActive.value = !isTrackerFilterActive.value
|
||||
}
|
||||
|
||||
function resetTrackerFilter() {
|
||||
trackerFilter.value = []
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-menu close-delay="0" open-delay="0" open-on-click open-on-hover>
|
||||
<v-menu close-delay="0" open-delay="0" open-on-click open-on-hover open-on-focus>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-slide-x-transition>
|
||||
<v-chip v-if="filterCount > 0" v-bind="props" class="ml-6" color="primary" variant="elevated" closable @click:close="resetAllFilters()">
|
||||
{{ t('navbar.top.active_filters.menu_label', filterCount) }}
|
||||
<v-chip v-if="filterPresentCount > 0" v-bind="props" class="ml-6" :color="globalFilterColor" variant="elevated"
|
||||
closable @click:close="resetAllFilters()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleAllFilters()">{{ globalFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.menu_label', filterActiveCount) }}
|
||||
</v-chip>
|
||||
</v-slide-x-transition>
|
||||
</template>
|
||||
|
||||
<div class="d-flex flex-column gap mt-3">
|
||||
<v-chip v-if="isTextFilterActive" variant="elevated" color="grey" closable @click:close="resetTextFilter()">
|
||||
{{ t('navbar.top.active_filters.text', { value: dashboardStore.searchFilter }) }}
|
||||
<v-chip v-if="isTextFilterPresent" :color="textFilterColor" variant="elevated"
|
||||
closable @click:close="resetTextFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTextFilter()">{{ isTextFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.text', { value: textFilter }) }}
|
||||
</v-chip>
|
||||
|
||||
<v-chip v-if="isStatusFilterActive && statusFilter.length === 1" :color="'torrent-' + statusFilter[0]" variant="elevated" closable @click:close="resetStatusFilter()">
|
||||
{{ t('navbar.top.active_filters.state', { value: t(`torrent.state.${statusFilter[0]}`) }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else-if="isStatusFilterActive" variant="elevated" closable @click:close="resetStatusFilter()">
|
||||
{{ t('navbar.top.active_filters.multiple_state', statusFilter.length) }}
|
||||
</v-chip>
|
||||
<template v-if="isStatusFilterPresent">
|
||||
<v-chip v-if="statusFilter.length === 1" :color="singleStatusFilterColor" variant="elevated"
|
||||
closable @click:close="resetStatusFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{ isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.state', { value: t(`torrent.state.${ statusFilter[0] }`) }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else :color="statusFilterColor" variant="elevated"
|
||||
closable @click:close="resetStatusFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{ isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_state', statusFilter.length) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<v-chip v-if="isCategoryFilterActive && categoryFilter.length === 1" color="category" variant="elevated" closable @click:close="resetCategoryFilter()">
|
||||
{{ t('navbar.top.active_filters.category', { value: categoryFilter[0] === '' ? t('navbar.side.filters.uncategorized') : categoryFilter[0] }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else-if="isCategoryFilterActive" color="category" variant="elevated" closable @click:close="resetCategoryFilter()">
|
||||
{{ t('navbar.top.active_filters.multiple_category', categoryFilter.length) }}
|
||||
</v-chip>
|
||||
<template v-if="isCategoryFilterPresent">
|
||||
<v-chip v-if="categoryFilter.length === 1" :color="categoryFilterColor" variant="elevated"
|
||||
closable @click:close="resetCategoryFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.category', { value: categoryFilter[0] === '' ? t('navbar.side.filters.uncategorized') : categoryFilter[0] })
|
||||
}}
|
||||
</v-chip>
|
||||
<v-chip v-else :color="categoryFilterColor" variant="elevated"
|
||||
closable @click:close="resetCategoryFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_category', categoryFilter.length) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<v-chip v-if="isTagFilterActive && tagFilter.length === 1" color="tag" variant="elevated" closable @click:close="resetTagFilter()">
|
||||
{{ t('navbar.top.active_filters.tag', { value: tagFilter[0] === null ? t('navbar.side.filters.untagged') : tagFilter[0] }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else-if="isTagFilterActive" color="tag" variant="elevated" closable @click:close="resetTagFilter()">
|
||||
{{ t('navbar.top.active_filters.multiple_tag', tagFilter.length) }}
|
||||
</v-chip>
|
||||
<template v-if="isTagFilterPresent">
|
||||
<v-chip v-if="tagFilter.length === 1" :color="tagFilterColor" variant="elevated"
|
||||
closable @click:close="resetTagFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{ isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.tag', { value: tagFilter[0] === null ? t('navbar.side.filters.untagged') : tagFilter[0] })
|
||||
}}
|
||||
</v-chip>
|
||||
<v-chip v-else :color="tagFilterColor" variant="elevated"
|
||||
closable @click:close="resetTagFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{ isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_tag', tagFilter.length) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<v-chip v-if="isTrackerFilterActive && trackerFilter.length === 1" color="tracker" variant="elevated" closable @click:close="resetTrackerFilter()">
|
||||
{{ t('navbar.top.active_filters.tracker', { value: trackerFilter[0] === '' ? t('navbar.side.filters.untracked') : trackerFilter[0] }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else-if="isTrackerFilterActive" color="tracker" variant="elevated" closable @click:close="resetTrackerFilter()">
|
||||
{{ t('navbar.top.active_filters.multiple_tracker', trackerFilter.length) }}
|
||||
</v-chip>
|
||||
<template v-if="isTrackerFilterPresent">
|
||||
<v-chip v-if="trackerFilter.length === 1" :color="trackerFilterColor" variant="elevated"
|
||||
closable @click:close="resetTrackerFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.tracker', { value: trackerFilter[0] === '' ? t('navbar.side.filters.untracked') : trackerFilter[0] })
|
||||
}}
|
||||
</v-chip>
|
||||
<v-chip v-else :color="trackerFilterColor" variant="elevated"
|
||||
closable @click:close="resetTrackerFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_tracker', trackerFilter.length) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</div>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { MaybeRefOrGetter, ref, toValue, watchEffect } from 'vue'
|
||||
|
||||
export function useSearchQuery<T>(items: MaybeRefOrGetter<T[]>, searchQuery: MaybeRefOrGetter<string>, getter: (item: T) => string, postProcess?: (items: T[]) => T[]) {
|
||||
export function useSearchQuery<T>(items: MaybeRefOrGetter<T[]>, searchQuery: MaybeRefOrGetter<string | null>, getter: (item: T) => string, postProcess?: (items: T[]) => T[]) {
|
||||
const results = ref<T[]>([])
|
||||
|
||||
watchEffect(() => {
|
||||
|
|
|
@ -21,7 +21,7 @@ const router = useRouter()
|
|||
const display = useDisplay()
|
||||
const dashboardStore = useDashboardStore()
|
||||
const dialogStore = useDialogStore()
|
||||
const { currentPage: dashboardPage, filteredTorrents, isSelectionMultiple, searchFilter, selectedTorrents, sortOptions, torrentCountString } = storeToRefs(useDashboardStore())
|
||||
const { currentPage: dashboardPage, filteredTorrents, isSelectionMultiple, selectedTorrents, sortOptions, torrentCountString } = storeToRefs(useDashboardStore())
|
||||
const maindataStore = useMaindataStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
|
@ -89,9 +89,9 @@ const trcProperties = reactive({
|
|||
})
|
||||
|
||||
const torrentTitleFilter = computed({
|
||||
get: () => searchFilter.value,
|
||||
get: () => maindataStore.textFilter,
|
||||
set: debounce((newValue: string) => {
|
||||
searchFilter.value = newValue
|
||||
maindataStore.textFilter = newValue
|
||||
}, 300)
|
||||
})
|
||||
|
||||
|
@ -100,7 +100,7 @@ const {
|
|||
currentPage,
|
||||
pageCount
|
||||
} = useArrayPagination(filteredTorrents, vuetorrentStore.paginationSize, dashboardPage)
|
||||
const hasSearchFilter = computed(() => !!searchFilter.value && searchFilter.value.length > 0)
|
||||
const hasSearchFilter = computed(() => !!maindataStore.textFilter && maindataStore.textFilter.length > 0)
|
||||
|
||||
const isAllTorrentsSelected = computed(() => filteredTorrents.value.length <= selectedTorrents.value.length)
|
||||
|
||||
|
@ -125,7 +125,7 @@ function goToInfo(hash: string) {
|
|||
}
|
||||
|
||||
function resetInput() {
|
||||
searchFilter.value = ''
|
||||
maindataStore.textFilter = ''
|
||||
}
|
||||
|
||||
function scrollToTop() {
|
||||
|
|
|
@ -15,11 +15,24 @@ const variables = {
|
|||
download: '#5BB974',
|
||||
upload: '#00B3FA',
|
||||
ratio: '#00B2F8',
|
||||
state: '#1E9367',
|
||||
category: '#04669A',
|
||||
tag: '#048B9A',
|
||||
tracker: '#C97D09',
|
||||
|
||||
// Active filters chip colors
|
||||
'active-global': '#4f738d',
|
||||
'active-global-disabled': '#35495E',
|
||||
'active-text': '#4f738d',
|
||||
'active-text-disabled': '#35495E',
|
||||
'active-status': '#4f738d',
|
||||
'active-status-disabled': '#35495E',
|
||||
'active-category': '#04669A',
|
||||
'active-category-disabled': '#02334d',
|
||||
'active-tag': '#048B9A',
|
||||
'active-tag-disabled': '#03464f',
|
||||
'active-tracker': '#C97D09',
|
||||
'active-tracker-disabled': '#6d4504',
|
||||
|
||||
// Torrent state colors
|
||||
'torrent-error': '#F83E70',
|
||||
'torrent-missingFiles': '#F83E70',
|
||||
|
|
|
@ -12,7 +12,6 @@ export const useDashboardStore = defineStore(
|
|||
'dashboard',
|
||||
() => {
|
||||
const currentPage = ref(1)
|
||||
const searchFilter = ref('')
|
||||
const filteredTorrents = computed(() => searchQuery.results.value)
|
||||
const isSelectionMultiple = ref(false)
|
||||
const selectedTorrents = ref<string[]>([])
|
||||
|
@ -28,7 +27,7 @@ export const useDashboardStore = defineStore(
|
|||
const vuetorrentStore = useVueTorrentStore()
|
||||
const searchQuery = useSearchQuery(
|
||||
() => maindataStore.torrentsWithFilters,
|
||||
searchFilter,
|
||||
() => maindataStore.isTextFilterActive ? maindataStore.textFilter : null,
|
||||
torrent => torrent.name,
|
||||
results => {
|
||||
if (sortOptions.isCustomSortEnabled) {
|
||||
|
@ -51,10 +50,10 @@ export const useDashboardStore = defineStore(
|
|||
const torrentCountString = computed(() => {
|
||||
if (selectedTorrents.value.length) {
|
||||
const selectedSize = selectedTorrents.value
|
||||
.map(hash => maindataStore.getTorrentByHash(hash))
|
||||
.filter(torrent => torrent !== undefined)
|
||||
.map(torrent => torrent!.size)
|
||||
.reduce((partial, size) => partial + size, 0)
|
||||
.map(hash => maindataStore.getTorrentByHash(hash))
|
||||
.filter(torrent => torrent !== undefined)
|
||||
.map(torrent => torrent!.size)
|
||||
.reduce((partial, size) => partial + size, 0)
|
||||
|
||||
return t('dashboard.selectedTorrentsCount', {
|
||||
count: selectedTorrents.value.length,
|
||||
|
@ -135,7 +134,6 @@ export const useDashboardStore = defineStore(
|
|||
|
||||
return {
|
||||
currentPage,
|
||||
searchFilter,
|
||||
filteredTorrents,
|
||||
isSelectionMultiple,
|
||||
selectedTorrents,
|
||||
|
@ -160,7 +158,7 @@ export const useDashboardStore = defineStore(
|
|||
{
|
||||
storage: localStorage,
|
||||
key: 'vuetorrent_dashboard',
|
||||
paths: ['searchFilter', 'sortOptions']
|
||||
paths: ['sortOptions']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { AddTorrentPayload } from '@/types/qbit/payloads'
|
|||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { generateMultiple } from '@/utils/faker'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, MaybeRefOrGetter, reactive, ref, toValue } from 'vue'
|
||||
import { computed, MaybeRefOrGetter, ref, toValue } from 'vue'
|
||||
|
||||
const isProduction = computed(() => process.env.NODE_ENV === 'production')
|
||||
|
||||
|
@ -25,22 +25,27 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
const torrents = ref<Torrent[]>([])
|
||||
const trackers = ref<string[]>([])
|
||||
|
||||
const filters = reactive({
|
||||
statusFilter: [] as TorrentState[],
|
||||
categoryFilter: [] as string[],
|
||||
tagFilter: [] as (string | null)[],
|
||||
trackerFilter: [] as (string | null)[]
|
||||
})
|
||||
const isTextFilterActive = ref(true)
|
||||
const isStatusFilterActive = ref(true)
|
||||
const isCategoryFilterActive = ref(true)
|
||||
const isTagFilterActive = ref(true)
|
||||
const isTrackerFilterActive = ref(true)
|
||||
|
||||
const textFilter = ref('')
|
||||
const statusFilter = ref<TorrentState[]>([])
|
||||
const categoryFilter = ref<string[]>([])
|
||||
const tagFilter = ref<(string | null)[]>([])
|
||||
const trackerFilter = ref<(string | null)[]>([])
|
||||
|
||||
const torrentsWithFilters = computed(() => {
|
||||
return torrents.value.filter(torrent => {
|
||||
if (filters.statusFilter.length > 0 && !filters.statusFilter.includes(torrent.state)) return false
|
||||
if (filters.categoryFilter.length > 0 && !filters.categoryFilter.includes(torrent.category)) return false
|
||||
if (filters.tagFilter.length > 0) {
|
||||
if (torrent.tags.length === 0 && filters.tagFilter.includes(null)) return true
|
||||
if (!torrent.tags.some(tag => filters.tagFilter.includes(tag))) return false
|
||||
if (statusFilter.value.length > 0 && isStatusFilterActive.value && !statusFilter.value.includes(torrent.state)) return false
|
||||
if (categoryFilter.value.length > 0 && isCategoryFilterActive.value && !categoryFilter.value.includes(torrent.category)) return false
|
||||
if (tagFilter.value.length > 0 && isTagFilterActive.value) {
|
||||
if (torrent.tags.length === 0 && tagFilter.value.includes(null)) return true
|
||||
if (!torrent.tags.some(tag => tagFilter.value.includes(tag))) return false
|
||||
}
|
||||
if (filters.trackerFilter.length > 0 && !filters.trackerFilter.includes(extractHostname(torrent.tracker))) return false
|
||||
if (trackerFilter.value.length > 0 && isTrackerFilterActive.value && !trackerFilter.value.includes(extractHostname(torrent.tracker))) return false
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
@ -167,10 +172,10 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
|
||||
if (vueTorrentStore.showTrackerFilter) {
|
||||
trackers.value = data
|
||||
.map(t => t.tracker)
|
||||
.map(url => extractHostname(url))
|
||||
.filter((domain, index, self) => index === self.indexOf(domain) && domain)
|
||||
.sort()
|
||||
.map(t => t.tracker)
|
||||
.map(url => extractHostname(url))
|
||||
.filter((domain, index, self) => index === self.indexOf(domain) && domain)
|
||||
.sort()
|
||||
}
|
||||
|
||||
// update torrents
|
||||
|
@ -324,7 +329,16 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
torrents,
|
||||
torrentsWithFilters,
|
||||
trackers,
|
||||
filters,
|
||||
isTextFilterActive,
|
||||
textFilter,
|
||||
isStatusFilterActive,
|
||||
statusFilter,
|
||||
isCategoryFilterActive,
|
||||
categoryFilter,
|
||||
isTagFilterActive,
|
||||
tagFilter,
|
||||
isTrackerFilterActive,
|
||||
trackerFilter,
|
||||
getTorrentByHash,
|
||||
getTorrentIndexByHash,
|
||||
deleteTorrents,
|
||||
|
@ -378,7 +392,18 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
{
|
||||
storage: localStorage,
|
||||
key: 'vuetorrent_maindata',
|
||||
paths: ['filters']
|
||||
paths: [
|
||||
'isTextFilterActive',
|
||||
'textFilter',
|
||||
'isStatusFilterActive',
|
||||
'statusFilter',
|
||||
'isCategoryFilterActive',
|
||||
'categoryFilter',
|
||||
'isTagFilterActive',
|
||||
'tagFilter',
|
||||
'isTrackerFilterActive',
|
||||
'trackerFilter',
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue