feat(dashboard): Add display mode configuration (#1184)

This commit is contained in:
Rémi Marseault 2023-11-29 11:50:26 +01:00 committed by GitHub
parent 6b84594885
commit cd695ab96d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 916 additions and 160 deletions

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import dayjs from '@/plugins/dayjs'
import dayjs from '@/plugins/dayjs.ts'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
import dayjs from '@/plugins/dayjs'
import dayjs from '@/plugins/dayjs.ts'
defineProps<{ torrent: Torrent; title: string; value: string }>()

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
import dayjs from '@/plugins/dayjs'
import dayjs from '@/plugins/dayjs.ts'
defineProps<{ torrent: Torrent; title: string; value: string }>()
</script>

View file

@ -0,0 +1,77 @@
<script setup lang="ts">
import { DashboardPropertyType } from '@/constants/vuetorrent'
import { useVueTorrentStore } from '@/stores'
import { useDashboardStore } from '@/stores/dashboard'
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
import ItemAmount from '@/components/Dashboard/DashboardItems/ItemAmount.vue'
import ItemChip from '@/components/Dashboard/DashboardItems/ItemChip.vue'
import ItemData from '@/components/Dashboard/DashboardItems/ItemData.vue'
import ItemDateTime from '@/components/Dashboard/DashboardItems/ItemDateTime.vue'
import ItemDuration from '@/components/Dashboard/DashboardItems/ItemDuration.vue'
import ItemPercent from '@/components/Dashboard/DashboardItems/ItemPercent.vue'
import ItemRelativeTime from '@/components/Dashboard/DashboardItems/ItemRelativeTime.vue'
import ItemSpeed from '@/components/Dashboard/DashboardItems/ItemSpeed.vue'
import ItemText from '@/components/Dashboard/DashboardItems/ItemText.vue'
const props = defineProps<{ torrent: Torrent }>()
defineEmits<{
onTorrentClick: [e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: Torrent]
}>()
const dashboardStore = useDashboardStore()
const vuetorrentStore = useVueTorrentStore()
const torrentProperties = computed(() => {
let ppts = props.torrent.progress === 1 ? vuetorrentStore.doneGridProperties : vuetorrentStore.busyGridProperties
return ppts.filter(ppt => ppt.active).sort((a, b) => a.order - b.order)
})
const getComponent = (type: DashboardPropertyType) => {
switch (type) {
case DashboardPropertyType.AMOUNT:
return ItemAmount
case DashboardPropertyType.CHIP:
return ItemChip
case DashboardPropertyType.DATA:
return ItemData
case DashboardPropertyType.DATETIME:
return ItemDateTime
case DashboardPropertyType.DURATION:
return ItemDuration
case DashboardPropertyType.PERCENT:
return ItemPercent
case DashboardPropertyType.RELATIVE:
return ItemRelativeTime
case DashboardPropertyType.SPEED:
return ItemSpeed
case DashboardPropertyType.TEXT:
default:
return ItemText
}
}
const isTorrentSelected = computed(() => dashboardStore.isTorrentInSelection(props.torrent.hash))
</script>
<template>
<v-card :class="`sideborder ${torrent.state} pointer`" height="100%"
:color="isTorrentSelected ? `torrent-${torrent.state}-darken-3` : undefined"
@click="$emit('onTorrentClick', $event, torrent)">
<v-card-title class="text-wrap text-subtitle-1 pt-1 pb-0">{{ torrent.name }}</v-card-title>
<v-card-text>
<div class="d-flex gap flex-wrap">
<component :is="getComponent(ppt.type)" :torrent="torrent" v-bind="ppt.props"
v-for="ppt in torrentProperties" />
</div>
</v-card-text>
</v-card>
</template>
<style scoped>
.gap {
gap: 8px 16px;
}
</style>

View file

@ -0,0 +1,49 @@
<script lang="ts" setup>
import GridTorrent from '@/components/Dashboard/Views/Grid/GridTorrent.vue'
import { useDashboardStore } from '@/stores/dashboard.ts'
import { Torrent as TorrentType } from '@/types/vuetorrent'
import { useDisplay } from 'vuetify'
defineProps<{
paginatedTorrents: TorrentType[]
}>()
defineEmits<{
onCheckboxClick: [torrent: TorrentType],
onTorrentClick: [e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: TorrentType],
onTorrentDblClick: [torrent: TorrentType],
onTorrentRightClick: [e: MouseEvent, torrent: TorrentType],
startPress: [e: Touch, torrent: TorrentType],
endPress: []
}>()
const display = useDisplay()
const dashboardStore = useDashboardStore()
</script>
<template>
<v-row id="torrentList">
<v-col v-for="torrent in paginatedTorrents" cols="12" lg="3" md="4" sm="6" xl="2"
:class="display.mobile ? 'pb-2' : 'pb-4'" class="pt-0"
@contextmenu="$emit('onTorrentRightClick', $event, torrent)"
@touchcancel="$emit('endPress')" @touchend="$emit('endPress')" @touchmove="$emit('endPress')"
@touchstart="$emit('startPress', $event.touches.item(0)!, torrent)"
@dblclick="$emit('onTorrentDblClick', torrent)">
<div class="d-flex align-center" style="height: 100%; width: 100%">
<v-expand-x-transition>
<v-btn v-show="dashboardStore.isSelectionMultiple"
:color="`torrent-${torrent.state}`"
:icon="dashboardStore.isTorrentInSelection(torrent.hash) ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'"
class="mr-2" variant="text" @click="$emit('onCheckboxClick', torrent)" />
</v-expand-x-transition>
<GridTorrent :torrent="torrent" @onTorrentClick="(e, t) => $emit('onTorrentClick', e, t)" />
</div>
</v-col>
</v-row>
</template>
<style scoped>
#torrentList {
background-color: unset;
}
</style>

View file

@ -1,21 +1,24 @@
<script setup lang="ts">
import ItemAmount from '@/components/Dashboard/DashboardItems/ItemAmount.vue'
import ItemChip from '@/components/Dashboard/DashboardItems/ItemChip.vue'
import ItemData from '@/components/Dashboard/DashboardItems/ItemData.vue'
import ItemDateTime from '@/components/Dashboard/DashboardItems/ItemDateTime.vue'
import ItemDuration from '@/components/Dashboard/DashboardItems/ItemDuration.vue'
import ItemPercent from '@/components/Dashboard/DashboardItems/ItemPercent.vue'
import ItemRelativeTime from '@/components/Dashboard/DashboardItems/ItemRelativeTime.vue'
import ItemSpeed from '@/components/Dashboard/DashboardItems/ItemSpeed.vue'
import ItemText from '@/components/Dashboard/DashboardItems/ItemText.vue'
import { DashboardPropertyType } from '@/constants/vuetorrent'
import { doesCommand } from '@/helpers'
import { useDashboardStore, useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
import ItemAmount from './DashboardItems/ItemAmount.vue'
import ItemChip from './DashboardItems/ItemChip.vue'
import ItemData from './DashboardItems/ItemData.vue'
import ItemDateTime from './DashboardItems/ItemDateTime.vue'
import ItemDuration from './DashboardItems/ItemDuration.vue'
import ItemPercent from './DashboardItems/ItemPercent.vue'
import ItemRelativeTime from './DashboardItems/ItemRelativeTime.vue'
import ItemSpeed from './DashboardItems/ItemSpeed.vue'
import ItemText from './DashboardItems/ItemText.vue'
const props = defineProps<{ torrent: Torrent }>()
defineEmits<{
onTorrentClick: [e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: Torrent]
}>()
const dashboardStore = useDashboardStore()
const vuetorrentStore = useVueTorrentStore()
@ -25,15 +28,6 @@ const torrentProperties = computed(() => {
return ppts.filter(ppt => ppt.active).sort((a, b) => a.order - b.order)
})
function onClick(e: MouseEvent) {
if (e.shiftKey) {
dashboardStore.spanTorrentSelection(props.torrent.hash)
} else if (doesCommand(e) || dashboardStore.isSelectionMultiple) {
dashboardStore.isSelectionMultiple = true
dashboardStore.toggleSelect(props.torrent.hash)
}
}
const getComponent = (type: DashboardPropertyType) => {
switch (type) {
case DashboardPropertyType.AMOUNT:
@ -61,7 +55,9 @@ const isTorrentSelected = computed(() => dashboardStore.isTorrentInSelection(pro
</script>
<template>
<v-card :class="`sideborder ${torrent.state} pointer`" :color="isTorrentSelected ? `torrent-${torrent.state}-darken-3` : undefined" width="100%" @click="onClick">
<v-card :class="`sideborder ${torrent.state} pointer`" width="100%"
:color="isTorrentSelected ? `torrent-${torrent.state}-darken-3` : undefined"
@click="$emit('onTorrentClick', $event, torrent)">
<v-card-title class="text-wrap text-subtitle-1 pt-1 pb-0">{{ torrent.name }}</v-card-title>
<v-card-text class="pa-2 pt-0">
<div class="d-flex gap flex-wrap">

View file

@ -0,0 +1,49 @@
<script lang="ts" setup>
import ListTorrent from '@/components/Dashboard/Views/List/ListTorrent.vue'
import { useDashboardStore } from '@/stores/dashboard'
import { Torrent as TorrentType } from '@/types/vuetorrent'
import { useDisplay } from 'vuetify'
defineProps<{
paginatedTorrents: TorrentType[]
}>()
defineEmits<{
onCheckboxClick: [torrent: TorrentType],
onTorrentClick: [e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: TorrentType],
onTorrentDblClick: [torrent: TorrentType],
onTorrentRightClick: [e: MouseEvent, torrent: TorrentType],
startPress: [e: Touch, torrent: TorrentType],
endPress: []
}>()
const display = useDisplay()
const dashboardStore = useDashboardStore()
</script>
<template>
<v-list id="torrentList" class="pa-0">
<v-list-item v-for="torrent in paginatedTorrents" :id="`torrent-${torrent.hash}`"
:class="display.mobile ? 'mb-2' : 'mb-4'" class="pa-0"
@contextmenu="$emit('onTorrentRightClick', $event, torrent)"
@touchcancel="$emit('endPress')" @touchend="$emit('endPress')" @touchmove="$emit('endPress')"
@touchstart="$emit('startPress', $event.touches.item(0)!, torrent)"
@dblclick="$emit('onTorrentDblClick', torrent)">
<div class="d-flex align-center">
<v-expand-x-transition>
<v-btn v-show="dashboardStore.isSelectionMultiple"
:color="`torrent-${torrent.state}`"
:icon="dashboardStore.isTorrentInSelection(torrent.hash) ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'"
class="mr-2" variant="text" @click="$emit('onCheckboxClick', torrent)" />
</v-expand-x-transition>
<ListTorrent :torrent="torrent" @onTorrentClick="(e, t) => $emit('onTorrentClick', e, t)" />
</div>
</v-list-item>
</v-list>
</template>
<style scoped>
#torrentList {
background-color: unset;
}
</style>

View file

@ -0,0 +1,11 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string; total: string }>()
</script>
<template>
<td>{{ torrent[value] }} / {{ torrent[total] }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,33 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
const props = defineProps<{ torrent: Torrent; title: string; value: string; color: string }>()
const values = computed(() => {
const val = props.torrent[props.value]
const type = typeof val
if (type === 'string') return val.length > 0 ? [val] : []
else if (type === 'object' /* array */) return val
})
</script>
<template>
<td>
<div class="d-flex flex-row gap">
<v-chip v-if="!values || values.length < 1" :color="color.replace('$1', torrent[value])" variant="flat">
{{ $t(`torrent.properties.empty_${value}`) }}
</v-chip>
<v-chip v-else v-for="val in values" :color="color.replace('$1', torrent.state)" variant="flat">
{{ val }}
</v-chip>
</div>
</td>
</template>
<style scoped>
.gap {
gap: 8px;
}
</style>

View file

@ -0,0 +1,15 @@
<script setup lang="ts">
import { formatData } from '@/helpers'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string }>()
const vuetorrentStore = useVueTorrentStore()
</script>
<template>
<td>{{ formatData(torrent[value], vuetorrentStore.useBinarySize) }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,18 @@
<script setup lang="ts">
import dayjs from '@/plugins/dayjs.ts'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string }>()
const vueTorrentStore = useVueTorrentStore()
</script>
<template>
<td v-if="torrent[value] > 0">
{{ dayjs(torrent[value] * 1000).format(vueTorrentStore.dateFormat ?? 'DD/MM/YYYY, HH:mm:ss') }}
</td>
<td v-else>{{ $t('dashboard.not_complete') }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,17 @@
<script setup lang="ts">
import dayjs from '@/plugins/dayjs.ts'
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string }>()
const durationFormat = 'D[d] H[h] m[m] s[s]'
</script>
<template>
<td v-if="torrent[value] > 0">
{{ dayjs.duration(torrent[value], 'seconds').format(durationFormat) }}
</td>
<td v-else>{{ $t('dashboard.not_complete') }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,33 @@
<script setup lang="ts">
import { TorrentState } from '@/constants/qbit'
import { formatPercent } from '@/helpers'
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
const props = defineProps<{ torrent: Torrent; title: string; value: string }>()
const isTorrentActive = computed(() => {
return [
TorrentState.UPLOADING,
TorrentState.CHECKING_UP,
TorrentState.FORCED_UP,
TorrentState.ALLOCATING,
TorrentState.DOWNLOADING,
TorrentState.META_DL,
TorrentState.CHECKING_DL,
TorrentState.FORCED_DL,
TorrentState.CHECKING_RESUME_DATA,
TorrentState.MOVING
].includes(props.torrent.state)
})
</script>
<template>
<td>
<v-progress-linear :model-value="torrent[value]" :max="1" :striped="isTorrentActive" :height="20" :color="`torrent-${torrent.state}`" rounded="sm" style="width: 10em">
{{ formatPercent(torrent[value]) }}
</v-progress-linear>
</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,12 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
import dayjs from '@/plugins/dayjs'
defineProps<{ torrent: Torrent; title: string; value: string }>()
</script>
<template>
<td>{{ dayjs(torrent[value] * 1000).fromNow() }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,15 @@
<script setup lang="ts">
import { formatSpeed } from '@/helpers'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string }>()
const vuetorrentStore = useVueTorrentStore()
</script>
<template>
<td>{{ formatSpeed(torrent[value], vuetorrentStore.useBitSpeed) }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,11 @@
<script setup lang="ts">
import { Torrent } from '@/types/vuetorrent'
defineProps<{ torrent: Torrent; title: string; value: string }>()
</script>
<template>
<td>{{ torrent[value] }}</td>
</template>
<style scoped></style>

View file

@ -0,0 +1,52 @@
<script setup lang="ts">
import { DashboardPropertyType } from '@/constants/vuetorrent'
import { useVueTorrentStore } from '@/stores'
import { Torrent as TorrentType } from '@/types/vuetorrent'
import { computed } from 'vue'
import ItemAmount from './DashboardItems/ItemAmount.vue'
import ItemChip from './DashboardItems/ItemChip.vue'
import ItemData from './DashboardItems/ItemData.vue'
import ItemDateTime from './DashboardItems/ItemDateTime.vue'
import ItemDuration from './DashboardItems/ItemDuration.vue'
import ItemPercent from './DashboardItems/ItemPercent.vue'
import ItemRelativeTime from './DashboardItems/ItemRelativeTime.vue'
import ItemSpeed from './DashboardItems/ItemSpeed.vue'
import ItemText from './DashboardItems/ItemText.vue'
defineProps<{ torrent: TorrentType }>()
const vuetorrentStore = useVueTorrentStore()
const torrentProperties = computed(() => vuetorrentStore.tableProperties.filter(ppt => ppt.active).sort((a, b) => a.order - b.order))
const getComponent = (type: DashboardPropertyType) => {
switch (type) {
case DashboardPropertyType.AMOUNT:
return ItemAmount
case DashboardPropertyType.CHIP:
return ItemChip
case DashboardPropertyType.DATA:
return ItemData
case DashboardPropertyType.DATETIME:
return ItemDateTime
case DashboardPropertyType.DURATION:
return ItemDuration
case DashboardPropertyType.PERCENT:
return ItemPercent
case DashboardPropertyType.RELATIVE:
return ItemRelativeTime
case DashboardPropertyType.SPEED:
return ItemSpeed
case DashboardPropertyType.TEXT:
default:
return ItemText
}
}
</script>
<template>
<component v-for="ppt in torrentProperties" :is="getComponent(ppt.type)"
:torrent="torrent" v-bind="ppt.props" />
</template>
<style scoped></style>

View file

@ -0,0 +1,93 @@
<script lang="ts" setup>
import TableTorrent from '@/components/Dashboard/Views/Table/TableTorrent.vue'
import { useVueTorrentStore } from '@/stores'
import { useDashboardStore } from '@/stores/dashboard.ts'
import { Torrent as TorrentType } from '@/types/vuetorrent'
import { computed } from 'vue'
defineProps<{
paginatedTorrents: TorrentType[]
}>()
defineEmits<{
onCheckboxClick: [torrent: TorrentType],
onTorrentClick: [e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: TorrentType],
onTorrentDblClick: [torrent: TorrentType],
onTorrentRightClick: [e: MouseEvent, torrent: TorrentType],
startPress: [e: Touch, torrent: TorrentType],
endPress: []
}>()
const dashboardStore = useDashboardStore()
const vuetorrentStore = useVueTorrentStore()
const torrentProperties = computed(() => vuetorrentStore.tableProperties.filter(ppt => ppt.active).sort((a, b) => a.order - b.order))
function isTorrentSelected(torrent: TorrentType) {
return dashboardStore.isTorrentInSelection(torrent.hash)
}
const getTorrentRowColorClass = (torrent: TorrentType) => [
'pointer',
isTorrentSelected(torrent) ? `bg-torrent-${ torrent.state }-darken-3 selected` : ''
]
</script>
<template>
<v-table id="torrentList" class="pa-0" density="compact">
<thead>
<tr>
<th class="px-1" />
<th v-if="dashboardStore.isSelectionMultiple" />
<th class="text-left">{{ $t('torrent.properties.name') }}</th>
<th v-for="ppt in torrentProperties" class="text-left">
{{ $t(`torrent.properties.${ ppt.props.title }`) }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="torrent in paginatedTorrents" :class="getTorrentRowColorClass(torrent)" v-ripple
@contextmenu="$emit('onTorrentRightClick', $event, torrent)"
@touchcancel="$emit('endPress')" @touchend="$emit('endPress')" @touchmove="$emit('endPress')"
@touchstart="$emit('startPress', $event.touches.item(0)!, torrent)"
@click="$emit('onTorrentClick', $event, torrent)"
@dblclick="$emit('onTorrentDblClick', torrent)">
<td :class="`pa-0 bg-torrent-${ torrent.state }`" />
<td v-if="dashboardStore.isSelectionMultiple">
<v-checkbox-btn :model-value="isTorrentSelected(torrent)"
:color="`torrent-${torrent.state}`" variant="text"
@click.stop="$emit('onCheckboxClick', torrent)" />
</td>
<td>{{ torrent.name }}</td>
<TableTorrent :torrent="torrent" />
</tr>
</tbody>
</v-table>
</template>
<style scoped lang="scss">
@use 'vuetify/settings';
#torrentList {
background-color: unset;
tbody tr:nth-child(odd) {
background-color: settings.$card-background;
}
tbody tr.selected {
position: relative;
&:nth-child(odd)::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
}
}
</style>

View file

@ -11,9 +11,12 @@ const authStore = useAuthStore()
const dndZoneRef = ref<HTMLDivElement>()
function onDragEnter() {
const routeName = route.name as string
const tabParam = route.params.tab as string
const subtabParam = route.params.subtab as string
if (
(route.name as string) === 'login' ||
((route.name as string) === 'settings' && route.params.tab === 'vuetorrent' && route.params.subtab === 'torrentCard') ||
(routeName) === 'login' ||
((routeName) === 'settings' && tabParam === 'vuetorrent' && subtabParam.startsWith('torrentCard')) ||
!authStore.isAuthenticated
)
return
@ -25,12 +28,11 @@ function onDrop(files: File[] | null, event: DragEvent) {
event.stopPropagation()
if (!event.dataTransfer) return
// Handle .torrent files
const torrentFiles = (files || []).filter(file => file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent'))
const links = event.dataTransfer
.getData('text/plain')
.split('\n')
.filter(link => link.startsWith('magnet:') || link.startsWith('http'))
const torrentFiles = (files || [])
.filter(file => file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent'))
const links = event.dataTransfer.getData('text/plain').split('\n')
.filter(link => link.startsWith('magnet:') || link.startsWith('http'))
torrentFiles.forEach(addTorrentStore.pushTorrentToQueue)
links.forEach(addTorrentStore.pushTorrentToQueue)

View file

@ -18,7 +18,16 @@ const titleOptionsList = [
{ title: t('constants.titleOptions.custom'), value: TitleOptions.CUSTOM }
]
const paginationSizes = ref([{ title: t('settings.vuetorrent.general.paginationSize.infinite_scroll'), value: -1 }, 5, 15, 30, 50])
const paginationSizes = ref([
{ title: t('settings.vuetorrent.general.paginationSize.infinite_scroll'), value: -1 },
5,
15,
30,
50,
100,
250,
500
])
const theme = computed({
get() {

View file

@ -0,0 +1,60 @@
<script setup lang="ts">
import DashboardItem from '@/components/Settings/VueTorrent/DashboardItem.vue'
import { TorrentProperty } from '@/constants/vuetorrent'
import { useVueTorrentStore } from '@/stores'
import { computed } from 'vue'
import Draggable from 'vuedraggable'
const vueTorrentStore = useVueTorrentStore()
const busyProperties = computed({
get: () => vueTorrentStore.busyGridProperties,
set: newValue => vueTorrentStore.updateBusyGridProperties(newValue)
})
const doneProperties = computed({
get: () => vueTorrentStore.doneGridProperties,
set: newValue => vueTorrentStore.updateDoneGridProperties(newValue)
})
function toggleActive(isBusy: boolean, property: TorrentProperty) {
if (isBusy) {
vueTorrentStore.toggleBusyGridProperty(property.name)
} else {
vueTorrentStore.toggleDoneGridProperty(property.name)
}
}
</script>
<template>
<v-row>
<v-col cols="12" md="6">
<v-list>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.grid.busyTip') }}</v-list-subheader>
<v-table>
<draggable v-model="busyProperties" item-key="name" tag="tbody" handle=".dnd-handle">
<template v-slot:item="{ element }">
<DashboardItem :property="element" @update="toggleActive(true, element)" />
</template>
</draggable>
</v-table>
</v-list>
</v-col>
<v-col cols="12" md="6">
<v-list>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.grid.doneTip') }}</v-list-subheader>
<v-table>
<draggable v-model="doneProperties" item-key="name" tag="tbody" handle=".dnd-handle">
<template v-slot:item="{ element }">
<DashboardItem :property="element" @update="toggleActive(false, element)" />
</template>
</draggable>
</v-table>
</v-list>
</v-col>
</v-row>
</template>
<style scoped lang="scss"></style>

View file

@ -29,7 +29,7 @@ function toggleActive(isBusy: boolean, property: TorrentProperty) {
<v-row>
<v-col cols="12" md="6">
<v-list>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.busyTorrentTip') }}</v-list-subheader>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.list.busyTip') }}</v-list-subheader>
<v-table>
<draggable v-model="busyTorrentProperties" item-key="name" tag="tbody" handle=".dnd-handle">
@ -43,7 +43,7 @@ function toggleActive(isBusy: boolean, property: TorrentProperty) {
<v-col cols="12" md="6">
<v-list>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.doneTorrentTip') }}</v-list-subheader>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.list.doneTip') }}</v-list-subheader>
<v-table>
<draggable v-model="doneTorrentProperties" item-key="name" tag="tbody" handle=".dnd-handle">

View file

@ -0,0 +1,38 @@
<script setup lang="ts">
import DashboardItem from '@/components/Settings/VueTorrent/DashboardItem.vue'
import { TorrentProperty } from '@/constants/vuetorrent'
import { useVueTorrentStore } from '@/stores'
import { computed } from 'vue'
import Draggable from 'vuedraggable'
const vueTorrentStore = useVueTorrentStore()
const busyTorrentProperties = computed({
get: () => vueTorrentStore.tableProperties,
set: newValue => vueTorrentStore.updateTableProperties(newValue)
})
function toggleActive(property: TorrentProperty) {
vueTorrentStore.toggleTableProperty(property.name)
}
</script>
<template>
<v-row>
<v-col cols="12">
<v-list>
<v-list-subheader>{{ $t('settings.vuetorrent.torrentCard.table.tip') }}</v-list-subheader>
<v-table>
<draggable v-model="busyTorrentProperties" item-key="name" tag="tbody" handle=".dnd-handle">
<template v-slot:item="{ element }">
<DashboardItem :property="element" @update="toggleActive(element)" />
</template>
</draggable>
</v-table>
</v-list>
</v-col>
</v-row>
</template>
<style scoped lang="scss"></style>

View file

@ -0,0 +1,5 @@
export enum DashboardDisplayMode {
LIST = 'list',
GRID = 'grid',
TABLE = 'table'
}

View file

@ -1,9 +1,23 @@
import type { TorrentProperty, PropertyData, PropertyMetadata } from './DashboardDefaults'
import type { PropertyData, PropertyMetadata, TorrentProperty } from './DashboardDefaults'
import { propsData, propsMetadata } from './DashboardDefaults'
import { DashboardDisplayMode } from './DashboardDisplayMode'
import { DashboardProperty } from './DashboardProperty'
import { DashboardPropertyType } from './DashboardPropertyType'
import { typesMap, getFileIcon } from './FileIcon'
import { getFileIcon, typesMap } from './FileIcon'
import { HistoryKey } from './HistoryKey'
import { TitleOptions } from './TitleOptions'
export { TorrentProperty, PropertyData, PropertyMetadata, propsData, propsMetadata, DashboardProperty, DashboardPropertyType, typesMap, getFileIcon, HistoryKey, TitleOptions }
export {
TorrentProperty,
PropertyData,
PropertyMetadata,
propsData,
propsMetadata,
DashboardDisplayMode,
DashboardProperty,
DashboardPropertyType,
getFileIcon,
typesMap,
HistoryKey,
TitleOptions
}

View file

@ -11,6 +11,6 @@ export const isMac = window.navigator.userAgent.toLowerCase().includes('mac')
/**
* Check Ctrl/Cmd key
*/
export function doesCommand(e: KeyboardEvent | MouseEvent | TouchEvent): boolean {
export function doesCommand(e: { metaKey: boolean, ctrlKey: boolean }): boolean {
return isMac ? e.metaKey : e.ctrlKey
}

View file

@ -1,23 +1,30 @@
<script lang="ts" setup>
import Torrent from '@/components/Dashboard/Torrent.vue'
import RightClickMenu from '@/components/Dashboard/TRC/RightClickMenu.vue'
import GridView from '@/components/Dashboard/Views/Grid/GridView.vue'
import ListView from '@/components/Dashboard/Views/List/ListView.vue'
import TableView from '@/components/Dashboard/Views/Table/TableView.vue'
import ConfirmDeleteDialog from '@/components/Dialogs/ConfirmDeleteDialog.vue'
import { useArrayPagination } from '@/composables'
import { DashboardDisplayMode } from '@/constants/vuetorrent'
import { doesCommand } from '@/helpers'
import { useDashboardStore, useDialogStore, useMaindataStore, useTorrentStore, useVueTorrentStore } from '@/stores'
import { Torrent as TorrentType } from '@/types/vuetorrent'
import debounce from 'lodash.debounce'
import { storeToRefs } from 'pinia'
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { computed, mergeProps, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useDisplay } from 'vuetify'
const { t } = useI18n()
const router = useRouter()
const display = useDisplay()
const { currentPage: dashboardPage, isSelectionMultiple, selectedTorrents, torrentCountString } = storeToRefs(useDashboardStore())
const dashboardStore = useDashboardStore()
const {
currentPage: dashboardPage,
isSelectionMultiple,
selectedTorrents,
displayMode,
torrentCountString
} = storeToRefs(dashboardStore)
const dialogStore = useDialogStore()
const maindataStore = useMaindataStore()
const torrentStore = useTorrentStore()
@ -94,7 +101,15 @@ const torrentTitleFilter = computed({
}, 300)
})
const { paginatedResults: paginatedTorrents, currentPage, pageCount } = useArrayPagination(filteredTorrents, vuetorrentStore.paginationSize, dashboardPage)
const isListView = computed(() => displayMode.value === DashboardDisplayMode.LIST)
const isGridView = computed(() => displayMode.value === DashboardDisplayMode.GRID)
const isTableView = computed(() => displayMode.value === DashboardDisplayMode.TABLE)
const {
paginatedResults: paginatedTorrents,
currentPage,
pageCount
} = useArrayPagination(filteredTorrents, vuetorrentStore.paginationSize, dashboardPage)
const hasSearchFilter = computed(() => !!torrentStore.textFilter && torrentStore.textFilter.length > 0)
const isAllTorrentsSelected = computed(() => filteredTorrents.value.length <= selectedTorrents.value.length)
@ -109,16 +124,6 @@ function toggleSearchFilter(forceState?: boolean) {
}
}
function toggleSelectTorrent(hash: string) {
dashboardStore.toggleSelect(hash)
}
function goToInfo(hash: string) {
if (!isSelectionMultiple.value) {
router.push({ name: 'torrentDetail', params: { hash } })
}
}
function resetInput() {
torrentStore.textFilter = ''
}
@ -142,9 +147,26 @@ function toggleSelectAll() {
}
}
async function onRightClick(e: PointerEvent, torrent: TorrentType) {
e.preventDefault()
function goToInfo(torrent: TorrentType) {
if (!isSelectionMultiple.value) {
router.push({ name: 'torrentDetail', params: { hash: torrent.hash } })
}
}
function onCheckboxClick(torrent: TorrentType) {
dashboardStore.toggleSelect(torrent.hash)
}
function onTorrentClick(e: { shiftKey: boolean, metaKey: boolean, ctrlKey: boolean }, torrent: TorrentType) {
if (e.shiftKey) {
dashboardStore.spanTorrentSelection(torrent.hash)
} else if (doesCommand(e) || dashboardStore.isSelectionMultiple) {
dashboardStore.isSelectionMultiple = true
dashboardStore.toggleSelect(torrent.hash)
}
}
async function onTorrentRightClick(e: MouseEvent | Touch, torrent: TorrentType) {
if (trcProperties.isVisible) {
trcProperties.isVisible = false
await nextTick()
@ -161,14 +183,19 @@ async function onRightClick(e: PointerEvent, torrent: TorrentType) {
}
}
watch(
() => trcProperties.isVisible,
newValue => {
if (!newValue && !isSelectionMultiple.value) {
dashboardStore.unselectAllTorrents()
}
}
)
// mobile long press
const timer = ref<NodeJS.Timeout>()
function startPress(e: Touch, torrent: TorrentType) {
timer.value = setTimeout(() => {
onTorrentRightClick(e, torrent)
}, 500)
}
function endPress() {
clearTimeout(timer.value)
}
// END mobile long press
function handleKeyboardShortcuts(e: KeyboardEvent) {
if (dialogStore.hasActiveDialog) {
@ -228,6 +255,15 @@ function handleKeyboardShortcuts(e: KeyboardEvent) {
}
}
watch(
() => trcProperties.isVisible,
newValue => {
if (!newValue && !isSelectionMultiple.value) {
dashboardStore.unselectAllTorrents()
}
}
)
onBeforeMount(async () => {
await maindataStore.fetchCategories()
await maindataStore.fetchTags()
@ -243,19 +279,6 @@ onMounted(() => {
onBeforeUnmount(() => {
document.removeEventListener('keydown', handleKeyboardShortcuts)
})
// mobile long press
const timer = ref<NodeJS.Timeout>()
function startPress(e: PointerEvent, torrent: TorrentType) {
timer.value = setTimeout(() => {
onRightClick(e, torrent)
}, 500)
}
function endPress() {
clearTimeout(timer.value)
}
</script>
<template>
@ -288,6 +311,27 @@ function endPress() {
<v-btn :icon="isSelectionMultiple ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'" v-bind="props" variant="plain" @click="toggleSelectMode" />
</template>
</v-tooltip>
<v-menu>
<template v-slot:activator="{ props: menu }">
<v-tooltip :text="$t('dashboard.displayMode.title')" location="top">
<template v-slot:activator="{ props: tooltip }">
<v-btn icon v-bind="mergeProps(menu, tooltip)" variant="plain">
<v-icon v-if="displayMode === DashboardDisplayMode.LIST" icon="mdi-view-list" />
<v-icon v-if="displayMode === DashboardDisplayMode.GRID" icon="mdi-view-grid" />
<v-icon v-if="displayMode === DashboardDisplayMode.TABLE" icon="mdi-table" />
</v-btn>
</template>
</v-tooltip>
</template>
<v-list>
<v-list-item :title="$t('dashboard.displayMode.list')" prepend-icon="mdi-view-list"
@click="displayMode = DashboardDisplayMode.LIST" />
<v-list-item :title="$t('dashboard.displayMode.grid')" prepend-icon="mdi-view-grid"
@click="displayMode = DashboardDisplayMode.GRID" />
<v-list-item :title="$t('dashboard.displayMode.table')" prepend-icon="mdi-table"
@click="displayMode = DashboardDisplayMode.TABLE" />
</v-list>
</v-menu>
<v-tooltip :text="t('dashboard.toggleSortOrder')" location="top">
<template v-slot:activator="{ props }">
<v-btn
@ -331,53 +375,40 @@ function endPress() {
</v-card>
</v-expand-transition>
</v-row>
<div v-if="filteredTorrents.length === 0" class="mt-5 text-xs-center">
<p class="text-grey">{{ t('common.emptyList') }}</p>
</div>
<div v-else>
<v-list id="torrentList" class="pa-0" color="transparent">
<v-list-item v-if="vuetorrentStore.isPaginationOnTop && !vuetorrentStore.isInfiniteScrollActive">
<v-pagination v-model="currentPage" :length="pageCount" next-icon="mdi-menu-right" prev-icon="mdi-menu-left" @input="scrollToTop" />
</v-list-item>
<v-list-item
v-for="torrent in paginatedTorrents"
:id="`torrent-${torrent.hash}`"
:class="display.mobile ? 'mb-2' : 'mb-4'"
class="pa-0"
@contextmenu="onRightClick($event, torrent)"
@touchcancel="endPress"
@touchend="endPress"
@touchmove="endPress"
@touchstart="startPress($event, torrent)"
@dblclick.prevent="goToInfo(torrent.hash)">
<div class="d-flex align-center">
<v-expand-x-transition>
<v-card v-show="isSelectionMultiple" class="mr-3" color="transparent">
<v-btn
:icon="dashboardStore.isTorrentInSelection(torrent.hash) ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'"
color="transparent"
variant="flat"
@click="toggleSelectTorrent(torrent.hash)" />
</v-card>
</v-expand-x-transition>
<Torrent :torrent="torrent" />
</div>
</v-list-item>
<div v-if="vuetorrentStore.isPaginationOnTop && !vuetorrentStore.isInfiniteScrollActive">
<v-pagination v-model="currentPage" :length="pageCount"
next-icon="mdi-menu-right" prev-icon="mdi-menu-left" @input="scrollToTop" />
</div>
<v-list-item v-if="!vuetorrentStore.isPaginationOnTop && !vuetorrentStore.isInfiniteScrollActive">
<v-pagination v-model="currentPage" :length="pageCount" next-icon="mdi-menu-right" prev-icon="mdi-menu-left" @input="scrollToTop" />
</v-list-item>
</v-list>
<ListView v-if="isListView" :paginated-torrents="paginatedTorrents"
@onTorrentClick="onTorrentClick" @onTorrentDblClick="goToInfo"
@onCheckboxClick="onCheckboxClick" @onTorrentRightClick="onTorrentRightClick"
@startPress="startPress" @endPress="endPress" />
<GridView v-else-if="isGridView" class="mb-2" :paginated-torrents="paginatedTorrents"
@onTorrentClick="onTorrentClick" @onTorrentDblClick="goToInfo"
@onCheckboxClick="onCheckboxClick" @onTorrentRightClick="onTorrentRightClick"
@startPress="startPress" @endPress="endPress" />
<TableView v-else-if="isTableView" :paginated-torrents="paginatedTorrents"
@onTorrentClick="onTorrentClick" @onTorrentDblClick="goToInfo"
@onCheckboxClick="onCheckboxClick" @onTorrentRightClick="onTorrentRightClick"
@startPress="startPress" @endPress="endPress" />
<div v-if="!vuetorrentStore.isPaginationOnTop && !vuetorrentStore.isInfiniteScrollActive">
<v-pagination v-model="currentPage" :length="pageCount"
next-icon="mdi-menu-right" prev-icon="mdi-menu-left" @input="scrollToTop" />
</div>
</div>
<div :style="`position: absolute; left: ${trcProperties.offset[0]}px; top: ${trcProperties.offset[1]}px;`">
<RightClickMenu v-model="trcProperties.isVisible" />
</div>
</template>
<style>
#torrentList {
background-color: unset;
}
<style scoped>
</style>

View file

@ -10,7 +10,9 @@ import RRules from '@/components/Settings/RSS/Rules.vue'
import Speed from '@/components/Settings/Speed.vue'
import TagsAndCategories from '@/components/Settings/TagsAndCategories.vue'
import VGeneral from '@/components/Settings/VueTorrent/General.vue'
import VTorrentCard from '@/components/Settings/VueTorrent/TorrentCard.vue'
import VTorrentCardList from '@/components/Settings/VueTorrent/TorrentCard/List.vue'
import VTorrentCardGrid from '@/components/Settings/VueTorrent/TorrentCard/Grid.vue'
import VTorrentCardTable from '@/components/Settings/VueTorrent/TorrentCard/Table.vue'
import WebUI from '@/components/Settings/WebUI.vue'
import { useDialogStore, usePreferenceStore } from '@/stores'
import { onBeforeUnmount, onMounted, ref, watchEffect } from 'vue'
@ -38,7 +40,9 @@ const tabs = [
const tabsV = [
{ text: t('settings.tabs.vuetorrent.general'), value: 'general' },
{ text: t('settings.tabs.vuetorrent.torrent_card'), value: 'torrentCard' }
{ text: t('settings.tabs.vuetorrent.torrent_card.list'), value: 'torrentCardList' },
{ text: t('settings.tabs.vuetorrent.torrent_card.grid'), value: 'torrentCardGrid' },
{ text: t('settings.tabs.vuetorrent.torrent_card.table'), value: 'torrentCardTable' }
]
const tabsR = [
@ -102,6 +106,7 @@ onMounted(() => {
document.addEventListener('keydown', handleKeyboardShortcut)
updateTabHandle()
})
onBeforeUnmount(() => {
document.removeEventListener('keydown', handleKeyboardShortcut)
})
@ -140,8 +145,14 @@ onBeforeUnmount(() => {
<v-window-item value="general">
<VGeneral />
</v-window-item>
<v-window-item value="torrentCard">
<VTorrentCard />
<v-window-item value="torrentCardList">
<VTorrentCardList />
</v-window-item>
<v-window-item value="torrentCardGrid">
<VTorrentCardGrid />
</v-window-item>
<v-window-item value="torrentCardTable">
<VTorrentCardTable />
</v-window-item>
</v-window>
</v-window-item>

View file

@ -1,3 +1,4 @@
import { DashboardDisplayMode } from '@/constants/vuetorrent'
import { formatData } from '@/helpers'
import { defineStore } from 'pinia'
import { computed, ref, watch } from 'vue'
@ -10,6 +11,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
const isSelectionMultiple = ref(false)
const selectedTorrents = ref<string[]>([])
const latestSelectedTorrent = ref<string>()
const displayMode = ref(DashboardDisplayMode.LIST)
const { t } = useI18n()
const torrentStore = useTorrentStore()
@ -100,22 +102,35 @@ export const useDashboardStore = defineStore('dashboard', () => {
if (pageCount < currentPage.value) {
currentPage.value = Math.max(1, pageCount)
}
}
)
})
return {
currentPage,
isSelectionMultiple,
selectedTorrents,
latestSelectedTorrent,
torrentCountString,
isTorrentInSelection,
selectTorrent,
selectTorrents,
unselectTorrent,
spanTorrentSelection,
selectAllTorrents,
unselectAllTorrents,
toggleSelect
return {
currentPage,
isSelectionMultiple,
selectedTorrents,
latestSelectedTorrent,
displayMode,
torrentCountString,
isTorrentInSelection,
selectTorrent,
selectTorrents,
unselectTorrent,
spanTorrentSelection,
selectAllTorrents,
unselectAllTorrents,
toggleSelect
}
},
{
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
key: 'vuetorrent_dashboard',
paths: ['displayMode']
}
]
}
}
})
)

View file

@ -129,17 +129,19 @@ export const useMaindataStore = defineStore('maindata', () => {
}
// update torrents
torrents.value = data.map(t => torrentBuilder.buildFromQbit(t))
const tempTorrents = data.map(t => torrentBuilder.buildFromQbit(t))
if (import.meta.env.DEV && import.meta.env.VITE_USE_FAKE_TORRENTS === 'true') {
const count = Number(import.meta.env.VITE_FAKE_TORRENT_COUNT)
const fakeTorrents: Partial<Torrent> = (await import('../../__mocks__/torrents.json')).default
for (let i = 1; i <= count; i++) {
torrents.value.push(torrentBuilder.buildFromFaker({ ...fakeTorrents.at(i), hash: uuidFromRaw(BigInt(i)) }, i))
for (let i = 0; i < count; i++) {
tempTorrents.push(torrentBuilder.buildFromFaker({ ...fakeTorrents.at(i), hash: uuidFromRaw(BigInt(i)) }, i))
}
}
torrents.value = tempTorrents
// filter out deleted torrents from selection
const hash_index = torrents.value.map(torrent => torrent.hash)
dashboardStore.selectedTorrents = dashboardStore.selectedTorrents.filter(hash => hash_index.includes(hash))

View file

@ -40,6 +40,11 @@ export const useVueTorrentStore = defineStore(
const _busyProperties = ref<PropertyData>(JSON.parse(JSON.stringify(propsData)))
const _doneProperties = ref<PropertyData>(JSON.parse(JSON.stringify(propsData)))
const _busyGridProperties = ref<PropertyData>(JSON.parse(JSON.stringify(propsData)))
const _doneGridProperties = ref<PropertyData>(JSON.parse(JSON.stringify(propsData)))
const _tableProperties = ref<PropertyData>(JSON.parse(JSON.stringify(propsData)))
const getCurrentThemeName = computed(() => (darkMode.value ? Theme.DARK : Theme.LIGHT))
const isInfiniteScrollActive = computed(() => paginationSize.value === -1)
@ -68,6 +73,44 @@ export const useVueTorrentStore = defineStore(
return formattedPpt
})
const busyGridProperties = computed<TorrentProperty[]>(() => {
const formattedPpt: TorrentProperty[] = new Array(Object.keys(propsData).length)
for (const [k, v] of Object.entries(_busyGridProperties.value)) {
formattedPpt[v.order - 1] = {
name: k as DashboardProperty,
...v,
...propsMetadata[k]
}
}
return formattedPpt
})
const doneGridProperties = computed<TorrentProperty[]>(() => {
const formattedPpt: TorrentProperty[] = new Array(Object.keys(propsData).length)
for (const [k, v] of Object.entries(_doneGridProperties.value)) {
formattedPpt[v.order - 1] = {
name: k as DashboardProperty,
...v,
...propsMetadata[k]
}
}
return formattedPpt
})
const tableProperties = computed<TorrentProperty[]>(() => {
const formattedPpt: TorrentProperty[] = new Array(Object.keys(propsData).length)
for (const [k, v] of Object.entries(_tableProperties.value)) {
formattedPpt[v.order - 1] = {
name: k as DashboardProperty,
...v,
...propsMetadata[k]
}
}
return formattedPpt
})
const i18n = useI18n()
const router = useRouter()
const theme = useTheme()
@ -122,6 +165,27 @@ export const useVueTorrentStore = defineStore(
})
}
function updateBusyGridProperties(values: TorrentProperty[]) {
values.forEach((ppt, index) => {
_busyGridProperties.value[ppt.name].active = ppt.active
_busyGridProperties.value[ppt.name].order = index + 1
})
}
function updateDoneGridProperties(values: TorrentProperty[]) {
values.forEach((ppt, index) => {
_doneGridProperties.value[ppt.name].active = ppt.active
_doneGridProperties.value[ppt.name].order = index + 1
})
}
function updateTableProperties(values: TorrentProperty[]) {
values.forEach((ppt, index) => {
_tableProperties.value[ppt.name].active = ppt.active
_tableProperties.value[ppt.name].order = index + 1
})
}
function toggleBusyProperty(name: DashboardProperty) {
_busyProperties.value[name].active = !_busyProperties.value[name].active
}
@ -130,6 +194,18 @@ export const useVueTorrentStore = defineStore(
_doneProperties.value[name].active = !_doneProperties.value[name].active
}
function toggleBusyGridProperty(name: DashboardProperty) {
_busyGridProperties.value[name].active = !_busyGridProperties.value[name].active
}
function toggleDoneGridProperty(name: DashboardProperty) {
_doneGridProperties.value[name].active = !_doneGridProperties.value[name].active
}
function toggleTableProperty(name: DashboardProperty) {
_tableProperties.value[name].active = !_tableProperties.value[name].active
}
return {
canvasRenderThreshold,
canvasRefreshThreshold,
@ -161,6 +237,12 @@ export const useVueTorrentStore = defineStore(
busyTorrentProperties,
_doneProperties,
doneTorrentProperties,
_busyGridProperties,
busyGridProperties,
_doneGridProperties,
doneGridProperties,
_tableProperties,
tableProperties,
getCurrentThemeName,
isInfiniteScrollActive,
setLanguage,
@ -170,8 +252,14 @@ export const useVueTorrentStore = defineStore(
redirectToLogin,
updateBusyProperties,
updateDoneProperties,
updateBusyGridProperties,
updateDoneGridProperties,
updateTableProperties,
toggleBusyProperty,
toggleDoneProperty
toggleDoneProperty,
toggleBusyGridProperty,
toggleDoneGridProperty,
toggleTableProperty
}
},
{

View file

@ -35,68 +35,68 @@ ul.no-bullet {
$sideborder-margin: 6px;
.sideborder {
border-left: #{$sideborder-margin} solid grey !important;
border-left: #{$sideborder-margin} solid grey;
&.error {
border-left-color: #{$torrent-error} !important;
border-left-color: #{$torrent-error};
}
&.missingFiles {
border-left-color: #{$torrent-missingFiles} !important;
border-left-color: #{$torrent-missingFiles};
}
&.uploading {
border-left-color: #{$torrent-uploading} !important;
border-left-color: #{$torrent-uploading};
}
&.forcedUP {
border-left-color: #{$torrent-forcedUP} !important;
border-left-color: #{$torrent-forcedUP};
}
&.pausedUP {
border-left-color: #{$torrent-pausedUP} !important;
border-left-color: #{$torrent-pausedUP};
}
&.queuedUP {
border-left-color: #{$torrent-queuedUP} !important;
border-left-color: #{$torrent-queuedUP};
}
&.stalledUP {
border-left-color: #{$torrent-stalledUP} !important;
border-left-color: #{$torrent-stalledUP};
}
&.checkingUP {
border-left-color: #{$torrent-checkingUP} !important;
border-left-color: #{$torrent-checkingUP};
}
&.allocating {
border-left-color: #{$torrent-allocating} !important;
border-left-color: #{$torrent-allocating};
}
&.downloading {
border-left-color: #{$torrent-downloading} !important;
border-left-color: #{$torrent-downloading};
}
&.forcedDL {
border-left-color: #{$torrent-forcedDL} !important;
border-left-color: #{$torrent-forcedDL};
}
&.metaDL {
border-left-color: #{$torrent-metaDL} !important;
border-left-color: #{$torrent-metaDL};
}
&.pausedDL {
border-left-color: #{$torrent-pausedDL} !important;
border-left-color: #{$torrent-pausedDL};
}
&.queuedDL {
border-left-color: #{$torrent-queuedDL} !important;
border-left-color: #{$torrent-queuedDL};
}
&.stalledDL {
border-left-color: #{$torrent-stalledDL} !important;
border-left-color: #{$torrent-stalledDL};
}
&.checkingDL {
border-left-color: #{$torrent-checkingDL} !important;
border-left-color: #{$torrent-checkingDL};
}
&.checkingResumeData {
border-left-color: #{$torrent-checkingResumeData} !important;
border-left-color: #{$torrent-checkingResumeData};
}
&.moving {
border-left-color: #{$torrent-moving} !important;
border-left-color: #{$torrent-moving};
}
&.unknown {
&.v-theme--darkTheme {
border-left-color: #{$torrent-unknown-dark} !important;
border-left-color: #{$torrent-unknown-dark};
}
&.v-theme-lightTheme {
border-left-color: #{$torrent-unknown-light} !important;
border-left-color: #{$torrent-unknown-light};
}
}
}