Skip to content

Commit

Permalink
perf: Add configurable format for duration values (#1826)
Browse files Browse the repository at this point in the history
  • Loading branch information
Larsluph authored Aug 3, 2024
1 parent fdab225 commit 831ac9b
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 76 deletions.
34 changes: 13 additions & 21 deletions src/components/Dashboard/DashboardItems/ItemDuration.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
<script setup lang="ts">
import dayjs from '@/plugins/dayjs'
import { formatDuration } from '@/helpers'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
import { DurationUnitType } from 'dayjs/plugin/duration'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const props = defineProps<{ torrent: Torrent; titleKey?: string; unit: DurationUnitType; value: (t: Torrent) => number }>()
const props = defineProps<{
torrent: Torrent;
titleKey?: string;
unit: DurationUnitType;
value: (t: Torrent) => number
}>()
const val = computed(() => props.value(props.torrent))
const formattedDuration = computed(() => {
const duration = dayjs.duration(val.value, props.unit)
const { durationFormat } = storeToRefs(useVueTorrentStore())
const durationValues = [duration.years(), duration.months(), duration.days(), duration.hours(), duration.minutes(), duration.seconds()]
const durationLabels = ['Y', 'M', 'd', 'h', 'm', 's']
let flag = false
return durationValues
.map((value, index) => {
if (flag || value) {
flag = true
return `${value}${durationLabels[index]}`
}
})
.filter(value => value)
.join(' ')
})
const val = computed(() => props.value(props.torrent))
</script>

<template>
Expand All @@ -33,7 +25,7 @@ const formattedDuration = computed(() => {
</div>
<div>
<span v-if="val > 0">
{{ formattedDuration }}
{{ formatDuration(val, props.unit, durationFormat) }}
</span>
<span v-else>{{ $t('common.NA') }}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
<script setup lang="ts">
import dayjs from '@/plugins/dayjs'
import { formatDuration } from '@/helpers'
import { useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
import { computed } from 'vue'
import { DurationUnitType } from 'dayjs/plugin/duration'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const props = defineProps<{ torrent: Torrent; unit: DurationUnitType; value: (t: Torrent) => number }>()
const val = computed(() => props.value(props.torrent))
const formattedDuration = computed(() => {
const duration = dayjs.duration(val.value, props.unit)
const { durationFormat } = storeToRefs(useVueTorrentStore())
const durationValues = [duration.years(), duration.months(), duration.days(), duration.hours(), duration.minutes(), duration.seconds()]
const durationLabels = ['Y', 'M', 'd', 'h', 'm', 's']
let flag = false
return durationValues
.map((value, index) => {
if (flag || value) {
flag = true
return `${value}${durationLabels[index]}`
}
})
.filter(value => value)
.join(' ')
})
const val = computed(() => props.value(props.torrent))
</script>

<template>
<td v-if="val > 0" class="text-no-wrap">
{{ formattedDuration }}
{{ formatDuration(val, props.unit, durationFormat) }}
</td>
<td v-else class="text-no-wrap">{{ $t('common.NA') }}</td>
</template>
33 changes: 29 additions & 4 deletions src/components/Settings/VueTorrent/General.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import ImportSettingsDialog from '@/components/Dialogs/ImportSettingsDialog.vue'
import { defaultDateFormat, TitleOptions } from '@/constants/vuetorrent'
import { defaultDateFormat, defaultDurationFormat, TitleOptions } from '@/constants/vuetorrent'
import { openLink } from '@/helpers'
import { LOCALES } from '@/locales'
import { Github } from '@/services/Github'
import { useAppStore, useDialogStore, useHistoryStore, useVueTorrentStore } from '@/stores'
Expand Down Expand Up @@ -115,8 +116,16 @@ const checkNewVersion = async () => {
toast.info(t('toast.version.new'))
}
function openDateFormatHelp() {
openLink('https://day.js.org/docs/en/display/format#list-of-all-available-formats')
}
function openDurationFormatHelp() {
openLink('https://day.js.org/docs/en/durations/format#list-of-all-available-formats')
}
function openBackendHelp() {
window.open('https://github.com/VueTorrent/vuetorrent-backend/wiki/Installation', '_blank', 'noreferrer')
openLink('https://github.com/VueTorrent/vuetorrent-backend/wiki/Installation')
}
</script>

Expand Down Expand Up @@ -223,8 +232,24 @@ function openBackendHelp() {
</v-row>

<v-row>
<v-col cols="12" md="6">
<v-text-field v-model="vueTorrentStore.dateFormat" :placeholder="defaultDateFormat" hint="using Dayjs" :label="t('settings.vuetorrent.general.dateFormat')" />
<v-col cols="12" md="3">
<v-text-field v-model="vueTorrentStore.dateFormat"
flat
hide-details
:label="t('settings.vuetorrent.general.dateFormat')"
:placeholder="defaultDateFormat"
append-inner-icon="mdi-help-circle"
@click:appendInner="openDateFormatHelp" />
</v-col>

<v-col cols="12" md="3">
<v-text-field v-model="vueTorrentStore.durationFormat"
flat
hide-details
:label="t('settings.vuetorrent.general.durationFormat')"
:placeholder="defaultDurationFormat"
append-inner-icon="mdi-help-circle"
@click:appendInner="openDurationFormatHelp" />
</v-col>

<v-col cols="12" md="6">
Expand Down
15 changes: 8 additions & 7 deletions src/components/TorrentDetail/Info/PanelDuration.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<script setup lang="ts">
import { useTorrentDetailStore } from '@/stores'
import { storeToRefs } from 'pinia'
import InfoBase from './InfoBase.vue'
import dayjs from '@/plugins/dayjs'
import { formatDuration } from '@/helpers'
import { useTorrentDetailStore, useVueTorrentStore } from '@/stores'
import { Torrent } from '@/types/vuetorrent'
import { DurationUnitType } from 'dayjs/plugin/duration'
import { storeToRefs } from 'pinia'
import InfoBase from './InfoBase.vue'
const props = defineProps<{ torrent: Torrent }>()
const { properties } = storeToRefs(useTorrentDetailStore())
const { durationFormat } = storeToRefs(useVueTorrentStore())
const torrentValues = [
const torrentValues: { title: string; unit: DurationUnitType; getter: () => number }[] = [
{ title: 'seeding_time', unit: 's', getter: () => props.torrent.seeding_time },
{ title: 'seeding_time_limit', unit: 'm', getter: () => props.torrent.seeding_time_limit },
{ title: 'inactive_seeding_time_limit', unit: 'm', getter: () => props.torrent.inactive_seeding_time_limit },
Expand All @@ -24,9 +25,9 @@ const torrentValues = [
<v-expansion-panel-text>
<v-row>
<InfoBase v-for="ppt in torrentValues">
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
<template v-if="ppt.getter() > 0" v-slot:text>
{{ dayjs.duration(ppt.getter(), ppt.unit as DurationUnitType).humanize() }}
{{ formatDuration(ppt.getter(), ppt.unit, durationFormat) }}
</template>
<template v-else v-slot:text>{{ $t('common.NA') }}</template>
</InfoBase>
Expand Down
4 changes: 3 additions & 1 deletion src/constants/vuetorrent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { TitleOptions } from './TitleOptions'
import { TorrentState, stateQbitToVt, stateVtToQbit } from './TorrentState'

const defaultDateFormat = 'YYYY-MM-DD HH:mm:ss'
const defaultDurationFormat = 'Y[Y] M[M] D[d] H[h] m[m] s[s]'

export {
comparatorMap,
Expand All @@ -32,5 +33,6 @@ export {
TorrentState,
stateQbitToVt,
stateVtToQbit,
defaultDateFormat
defaultDateFormat,
defaultDurationFormat
}
11 changes: 10 additions & 1 deletion src/helpers/datetime.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import timezoneMock from 'timezone-mock'
import { expect, test } from 'vitest'
import { formatEta, formatTimeMs, formatTimeSec } from './datetime'
import { formatDuration, formatEta, formatTimeMs, formatTimeSec } from './datetime'

beforeAll(() => {
timezoneMock.register('UTC')
Expand Down Expand Up @@ -45,3 +45,12 @@ test('helpers/datetime/formatTimeMs', () => {
test('helpers/datetime/formatTimeSec', () => {
expect(formatTimeSec(1626739200, 'YYYY-MM-DD')).toBe('2021-07-20')
})

test('helpers/datetime/formatDuration', () => {
expect(formatDuration(60, 's', 'HH:mm:ss')).toBe('00:01:00')
expect(formatDuration(1, 'm', 'HH:mm:ss')).toBe('00:01:00')
expect(formatDuration(1, 'h', 'HH:mm:ss')).toBe('01:00:00')
expect(formatDuration(1, 'd', 'D [days], HH:mm:ss')).toBe('1 days, 00:00:00')
expect(formatDuration(0, 's', 'HH:mm:ss')).toBe('00:00:00')
expect(formatDuration(1000000, 's', 'D [days], HH:mm:ss')).toBe('11 days, 13:46:40')
})
34 changes: 15 additions & 19 deletions src/helpers/datetime.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { defaultDateFormat } from '@/constants/vuetorrent'
import dayjs from '@/plugins/dayjs'
import { DurationUnitType } from 'dayjs/plugin/duration'

export function formatEta(value: number): string {
const options = { dayLimit: 100 }
const QBIT_MAX_ETA = 864_000 // 100 days
const MAX_UNITS = 2 // Will display 2 units max, from highest to lowest

if (value >= QBIT_MAX_ETA) {
return '∞'
}

const minute = 60
const hour = minute * 60
const day = hour * 24
Expand All @@ -15,25 +21,11 @@ export function formatEta(value: number): string {
let unitSize = 0
const parts = []

const defaultOptions = {
maxUnitSize: 2,
dayLimit: 0,
minUnit: 0
}

const opt = options ? Object.assign(defaultOptions, options) : defaultOptions

if (opt.dayLimit && value >= opt.dayLimit * day) {
return '∞'
}

while ((!opt.maxUnitSize || unitSize !== opt.maxUnitSize) && index !== durations.length) {
while (unitSize < MAX_UNITS && index !== durations.length) {
const duration = durations[index]
if (value < duration) {
index++
continue
} else if (opt.minUnit && durations.length - index <= opt.minUnit) {
break
}

const result = Math.floor(value / duration)
Expand All @@ -45,16 +37,20 @@ export function formatEta(value: number): string {
}

if (!parts.length) {
return '0' + units[durations.length - 1 - opt.minUnit]
return '0' + units[durations.length - 1]
}

return parts.join(' ')
}

export function formatTimeMs(value: number, format: string): string {
return dayjs(value).format(format ?? defaultDateFormat)
return dayjs(value).format(format)
}

export function formatTimeSec(value: number, format: string): string {
return formatTimeMs(value * 1000, format)
}

export function formatDuration(value: number, unit: DurationUnitType, format: string): string {
return dayjs.duration(value, unit).format(format)
}
6 changes: 4 additions & 2 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getColorFromName, getRatioColor, getTorrentStateColor, getTorrentStateValue } from './colors'
import comparators, { Comparator } from './comparators'
import { formatDataValue, formatDataUnit, formatData } from './data'
import { formatEta, formatTimeMs, formatTimeSec } from './datetime'
import { formatEta, formatTimeMs, formatTimeSec, formatDuration } from './datetime'
import { toPrecision, formatPercent } from './number'
import { basename } from './path'
import { formatSpeedValue, formatSpeedUnit, formatSpeed } from './speed'
import { isWindows, isMac, doesCommand } from './system'
import { isWindows, isMac, doesCommand, openLink } from './system'
import { titleCase, capitalize, extractHostname, getDomainBody, splitByUrl, stringContainsUrl, codeToFlag } from './text'

export {
Expand All @@ -20,6 +20,7 @@ export {
formatEta,
formatTimeMs,
formatTimeSec,
formatDuration,
toPrecision,
formatPercent,
basename,
Expand All @@ -29,6 +30,7 @@ export {
isWindows,
isMac,
doesCommand,
openLink,
titleCase,
capitalize,
extractHostname,
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ export const isMac = window.navigator.userAgent.toLowerCase().includes('mac')
export function doesCommand(e: { metaKey: boolean; ctrlKey: boolean }): boolean {
return isMac ? e.metaKey : e.ctrlKey
}

export function openLink(link: string) {
window.open(link, '_blank', 'noreferrer')
}
1 change: 1 addition & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,7 @@
"dateFormat": "Date Format",
"displayGraphLimits": "Display limits in graph",
"download": "Export settings",
"durationFormat": "Duration format",
"enableHashColors": "Enable generated chip colors",
"enableRatioColors": "Enable ratio colors",
"fileContentInterval": "Torrent file content refresh interval",
Expand Down
15 changes: 14 additions & 1 deletion src/stores/vuetorrent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { DashboardProperty, defaultDateFormat, PropertyData, propsData, propsMetadata, TitleOptions, TorrentProperty, ThemeMode } from '@/constants/vuetorrent'
import {
DashboardProperty,
defaultDateFormat,
PropertyData,
propsData,
propsMetadata,
TitleOptions,
TorrentProperty,
ThemeMode,
defaultDurationFormat
} from '@/constants/vuetorrent'
import { backendStorage } from '@/services/backend'
import { DarkLegacy, LightLegacy } from '@/themes'
import { useMediaQuery } from '@vueuse/core'
Expand Down Expand Up @@ -34,6 +44,7 @@ export const useVueTorrentStore = defineStore(
const enableHashColors = ref(true)
const paginationSize = ref(15)
const dateFormat = ref(defaultDateFormat)
const durationFormat = ref(defaultDurationFormat)
const isShutdownButtonVisible = ref(false)
const useBitSpeed = ref(false)
const useBinarySize = ref(false)
Expand Down Expand Up @@ -222,6 +233,7 @@ export const useVueTorrentStore = defineStore(
backendUrl,
theme,
dateFormat,
durationFormat,
deleteWithFiles,
fileContentInterval,
isDrawerRight,
Expand Down Expand Up @@ -291,6 +303,7 @@ export const useVueTorrentStore = defineStore(
enableHashColors.value = true
paginationSize.value = 15
dateFormat.value = defaultDateFormat
durationFormat.value = defaultDurationFormat
isShutdownButtonVisible.value = false
useBitSpeed.value = false
useBinarySize.value = false
Expand Down

0 comments on commit 831ac9b

Please sign in to comment.