Remove vue 2 filters

Fixes #1759
This commit is contained in:
Kasper Seweryn 2022-04-30 22:46:37 +02:00 committed by Georg Krause
parent 12b09b085a
commit b7d66232f6
48 changed files with 368 additions and 382 deletions

View File

@ -82,7 +82,6 @@
"vite": "2.8.6", "vite": "2.8.6",
"vite-plugin-pwa": "0.12.0", "vite-plugin-pwa": "0.12.0",
"vue-jest": "3.0.7", "vue-jest": "3.0.7",
"vue-template-compiler": "2.6.14",
"workbox-core": "6.5.3", "workbox-core": "6.5.3",
"workbox-precaching": "6.5.3", "workbox-precaching": "6.5.3",
"workbox-routing": "6.5.3", "workbox-routing": "6.5.3",

View File

@ -252,7 +252,7 @@
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { get } from 'lodash-es' import { get } from 'lodash-es'
import showdown from 'showdown' import showdown from 'showdown'
import { humanSize } from '~/init/filters' import { humanSize } from '~/utils/filters'
import SignupForm from '~/components/auth/SignupForm.vue' import SignupForm from '~/components/auth/SignupForm.vue'
import LogoText from '~/components/LogoText.vue' import LogoText from '~/components/LogoText.vue'

View File

@ -278,7 +278,7 @@ We render some markdown to html here, the content is set by the admin so we shou
class="right aligned" class="right aligned"
> >
<span class="features-status ui text"> <span class="features-status ui text">
{{ defaultUploadQuota * 1000 * 1000 | humanSize }} {{ humanSize(defaultUploadQuota * 1000 * 1000) }}
</span> </span>
</td> </td>
<td <td
@ -436,10 +436,15 @@ We render some markdown to html here, the content is set by the admin so we shou
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { get } from 'lodash-es' import { get } from 'lodash-es'
import showdown from 'showdown' import showdown from 'showdown'
import { humanSize } from '~/utils/filters'
export default { export default {
setup () {
return { humanSize }
},
data () { data () {
return { return {
// TODO (wvffle): Remove v-html
markdown: new showdown.Converter(), markdown: new showdown.Converter(),
showAllowedDomains: false showAllowedDomains: false
} }

View File

@ -332,7 +332,7 @@ import AlbumWidget from '~/components/audio/album/Widget.vue'
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue' import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
import LoginForm from '~/components/auth/LoginForm.vue' import LoginForm from '~/components/auth/LoginForm.vue'
import SignupForm from '~/components/auth/SignupForm.vue' import SignupForm from '~/components/auth/SignupForm.vue'
import { humanSize } from '~/init/filters' import { humanSize } from '~/utils/filters'
export default { export default {
components: { components: {

View File

@ -78,7 +78,7 @@
import PlayButton from '~/components/audio/PlayButton.vue' import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import { momentFormat } from '~/init/filters' import { momentFormat } from '~/utils/filters'
import moment from 'moment' import moment from 'moment'
export default { export default {

View File

@ -37,7 +37,7 @@
</div> </div>
</div> </div>
<div class="extra content"> <div class="extra content">
<span v-if="album.release_date">{{ album.release_date | moment('Y') }} · </span> <span v-if="album.release_date">{{ momentFormat(album.release_date, 'Y') }} · </span>
<translate <translate
translate-context="*/*/*" translate-context="*/*/*"
:translate-params="{count: album.tracks_count}" :translate-params="{count: album.tracks_count}"
@ -59,6 +59,7 @@
<script> <script>
import PlayButton from '~/components/audio/PlayButton.vue' import PlayButton from '~/components/audio/PlayButton.vue'
import { momentFormat} from '~/utils/filters'
export default { export default {
components: { components: {
@ -67,6 +68,9 @@ export default {
props: { props: {
album: { type: Object, required: true } album: { type: Object, required: true }
}, },
setup () {
return { momentFormat }
},
computed: { computed: {
imageUrl () { imageUrl () {
if (this.album.cover && this.album.cover.urls.original) { if (this.album.cover && this.album.cover.urls.original) {

View File

@ -22,7 +22,7 @@
class="discrete link" class="discrete link"
:to="{name: 'library.artists.detail', params: {id: artist.id}}" :to="{name: 'library.artists.detail', params: {id: artist.id}}"
> >
{{ artist.name|truncate(30) }} {{ truncate(artist.name, 30) }}
</router-link> </router-link>
</strong> </strong>
@ -67,6 +67,7 @@
<script> <script>
import PlayButton from '~/components/audio/PlayButton.vue' import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import { truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -74,6 +75,9 @@ export default {
TagsList TagsList
}, },
props: { artist: { type: Object, required: true } }, props: { artist: { type: Object, required: true } },
setup () {
return { truncate }
},
data () { data () {
return { return {
initialAlbums: 30, initialAlbums: 30,

View File

@ -158,12 +158,12 @@
</template> </template>
<script> <script>
import { clone } from 'lodash-es' import { clone, uniqBy } from 'lodash-es'
import axios from 'axios' import axios from 'axios'
import TrackRow from '~/components/audio/track/Row.vue' import TrackRow from '~/components/audio/track/Row.vue'
import TrackMobileRow from '~/components/audio/track/MobileRow.vue' import TrackMobileRow from '~/components/audio/track/MobileRow.vue'
import Pagination from '~/components/Pagination.vue' import Pagination from '~/components/Pagination.vue'
import { unique } from '~/init/filters' import { unique } from '~/utils/filters'
export default { export default {
components: { components: {
@ -205,7 +205,7 @@ export default {
computed: { computed: {
allTracks () { allTracks () {
const tracks = (this.tracks || []).concat(this.additionalTracks) const tracks = (this.tracks || []).concat(this.additionalTracks)
return unique(tracks, 'id') return uniqBy(tracks, 'id')
}, },
labels () { labels () {

View File

@ -137,7 +137,7 @@
</template> </template>
<div class="sub header"> <div class="sub header">
<template v-if="file.response.uuid"> <template v-if="file.response.uuid">
{{ file.size | humanSize }} {{ humanSize(file.size) }}
<template v-if="file.response.duration"> <template v-if="file.response.duration">
· <human-duration :duration="file.response.duration" /> · <human-duration :duration="file.response.duration" />
</template> </template>
@ -164,7 +164,7 @@
> >
Pending Pending
</translate> </translate>
· {{ file.size | humanSize }} · {{ humanSize(file.size) }}
· {{ parseInt(file.progress) }}% · {{ parseInt(file.progress) }}%
</template> </template>
· <a @click.stop.prevent="remove(file)"> · <a @click.stop.prevent="remove(file)">
@ -243,6 +243,7 @@ import LicenseSelect from '~/components/channels/LicenseSelect.vue'
import AlbumSelect from '~/components/channels/AlbumSelect.vue' import AlbumSelect from '~/components/channels/AlbumSelect.vue'
import FileUploadWidget from '~/components/library/FileUploadWidget.vue' import FileUploadWidget from '~/components/library/FileUploadWidget.vue'
import UploadMetadataForm from '~/components/channels/UploadMetadataForm.vue' import UploadMetadataForm from '~/components/channels/UploadMetadataForm.vue'
import { humanSize } from '~/utils/filters'
function setIfEmpty (obj, k, v) { function setIfEmpty (obj, k, v) {
if (obj[k] !== undefined) { if (obj[k] !== undefined) {
@ -261,6 +262,9 @@ export default {
props: { props: {
channel: { type: Object, default: null, required: false } channel: { type: Object, default: null, required: false }
}, },
setup () {
return { humanSize }
},
data () { data () {
return { return {
availableChannels: { availableChannels: {

View File

@ -55,7 +55,7 @@
<translate translate-context="Content/Library/Paragraph"> <translate translate-context="Content/Library/Paragraph">
Remaining storage space: Remaining storage space:
</translate> </translate>
{{ (statusData.quotaStatus.remaining * 1000 * 1000) - statusData.uploadedSize | humanSize }} {{ (statusData.quotaStatus.remaining * 1000 * 1000) - humanSize(statusData.uploadedSize) }}
</template> </template>
</div> </div>
<div class="ui hidden clearing divider mobile-only" /> <div class="ui hidden clearing divider mobile-only" />
@ -144,7 +144,7 @@
<script> <script>
import Modal from '~/components/semantic/Modal.vue' import Modal from '~/components/semantic/Modal.vue'
import ChannelUploadForm from '~/components/channels/UploadForm.vue' import ChannelUploadForm from '~/components/channels/UploadForm.vue'
import { humanSize } from '~/init/filters' import { humanSize } from '~/utils/filters'
import {onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router' import {onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router'
export default { export default {
@ -159,6 +159,8 @@ export default {
onBeforeRouteUpdate(guard) onBeforeRouteUpdate(guard)
onBeforeRouteLeave(guard) onBeforeRouteLeave(guard)
return { humanSize }
}, },
data () { data () {
return { return {

View File

@ -1,42 +1,64 @@
<script setup lang="ts">
import { toRefs } from '@vueuse/core'
import { computed } from 'vue'
import { truncate } from '~/utils/filters'
import { Actor } from '~/types'
interface Props {
actor: Actor
avatar?: boolean
admin?: boolean
displayName?: boolean
truncateLength?: number
}
const { displayName, actor, truncateLength, admin, avatar } = toRefs(withDefaults(
defineProps<Props>(),
{
avatar: true,
admin: false,
displayName: false,
truncateLength: 30
}
))
const repr = computed(() => {
const name = displayName.value || actor.value.is_local
? actor.value.preferred_username
: actor.value.full_username
return truncate(name, truncateLength.value)
})
const url = computed(() => {
if (admin.value) {
return { name: 'manage.moderation.accounts.detail', params: { id: actor.value.full_username } }
}
if (actor.value.is_local) {
return { name: 'profile.overview', params: { username: actor.value.preferred_username } }
}
return {
name: 'profile.full.overview',
params: {
username: actor.value.preferred_username,
domain: actor.value.domain
}
}
})
</script>
<template> <template>
<router-link <router-link
:to="url" :to="url"
:title="actor.full_username" :title="actor.full_username"
> >
<template v-if="avatar"> <actor-avatar
<actor-avatar :actor="actor" /><span>&nbsp;</span> v-if="avatar"
</template><slot>{{ repr | truncate(truncateLength) }}</slot> :actor="actor"
/>
<span>&nbsp;</span>
<slot>{{ repr }}</slot>
</router-link> </router-link>
</template> </template>
<script>
export default {
props: {
actor: { type: Object, required: true },
avatar: { type: Boolean, default: true },
admin: { type: Boolean, default: false },
displayName: { type: Boolean, default: false },
truncateLength: { type: Number, default: 30 }
},
computed: {
url () {
if (this.admin) {
return { name: 'manage.moderation.accounts.detail', params: { id: this.actor.full_username } }
}
if (this.actor.is_local) {
return { name: 'profile.overview', params: { username: this.actor.preferred_username } }
} else {
return { name: 'profile.full.overview', params: { username: this.actor.preferred_username, domain: this.actor.domain } }
}
},
repr () {
if (this.displayName || this.actor.is_local) {
return this.actor.preferred_username
} else {
return this.actor.full_username
}
}
}
}
</script>

View File

@ -1,26 +1,29 @@
<script setup lang="ts">
import moment from 'moment'
import { computed } from 'vue'
interface Props {
seconds?: number
}
const props = defineProps<Props>()
const duration = computed(() => {
const { minutes, hours } = moment.duration(props.seconds, 'seconds')
return { minutes: minutes(), hours: hours() }
})
</script>
<template> <template>
<span> <span>
<translate <translate
v-if="durationData.hours > 0" v-if="duration.hours > 0"
translate-context="Content/*/Paragraph" translate-context="Content/*/Paragraph"
:translate-params="{minutes: durationData.minutes, hours: durationData.hours}" :translate-params="duration"
>%{ hours } h %{ minutes } min</translate> >%{ hours } h %{ minutes } min</translate>
<translate <translate
v-else v-else
translate-context="Content/*/Paragraph" translate-context="Content/*/Paragraph"
:translate-params="{minutes: durationData.minutes}" :translate-params="duration"
>%{ minutes } min</translate> >%{ minutes } min</translate>
</span> </span>
</template> </template>
<script>
import { secondsToObject } from '~/init/filters'
export default {
props: { seconds: { type: Number, default: null } },
computed: {
durationData () {
return secondsToObject(this.seconds)
}
}
}
</script>

View File

@ -1,32 +1,32 @@
<script setup lang="ts">
import { momentFormat } from '~/utils/filters'
import { useTimeAgo } from '@vueuse/core'
import { computed } from 'vue'
interface Props {
date: string,
icon?: boolean
}
const props = withDefaults(
defineProps<Props>(),
{ icon: false }
)
const date = computed(() => new Date(props.date))
// TODO (wvffle): Translate useTimeAgo
const realDate = useTimeAgo(date)
</script>
<template> <template>
<time <time
:datetime="date" :datetime="date"
:title="date | moment" :title="momentFormat(date)"
> >
<i <i
v-if="icon" v-if="props.icon"
class="outline clock icon" class="outline clock icon"
/> />
{{ realDate | ago($store.state.ui.momentLocale) }} {{ realDate }}
</time> </time>
</template> </template>
<script>
import { mapState } from 'vuex'
export default {
props: {
date: { type: String, required: true },
icon: { type: Boolean, required: false, default: false }
},
computed: {
...mapState({
lastDate: state => state.ui.lastDate
}),
realDate () {
if (this.lastDate) {
// dummy code to trigger a recompute to update the ago render
}
return this.date
}
}
}
</script>

View File

@ -1,12 +1,18 @@
<script setup lang="ts">
import { toRefs } from '@vueuse/core'
import { computed } from 'vue'
import time from '~/utils/time'
interface Props {
duration: number
}
const { duration } = toRefs(defineProps<Props>())
const parsedDuration = computed(() => time.parse(duration.value))
</script>
<template> <template>
<time :datetime="`${duration}s`"> <time :datetime="`${duration}s`">
{{ duration | duration }} {{ parsedDuration }}
</time> </time>
</template> </template>
<script>
export default {
props: {
duration: { type: Number, required: true }
}
}
</script>

View File

@ -141,7 +141,7 @@
v-if="object.release_date || (totalTracks > 0)" v-if="object.release_date || (totalTracks > 0)"
class="ui small hidden divider" class="ui small hidden divider"
/> />
<span v-if="object.release_date">{{ object.release_date | moment('Y') }} · </span> <span v-if="object.release_date">{{ momentFormat(object.release_date, 'Y') }} · </span>
<template v-if="totalTracks > 0"> <template v-if="totalTracks > 0">
<translate <translate
v-if="isSerie" v-if="isSerie"
@ -254,6 +254,7 @@ import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import ArtistLabel from '~/components/audio/ArtistLabel.vue' import ArtistLabel from '~/components/audio/ArtistLabel.vue'
import AlbumDropdown from './AlbumDropdown.vue' import AlbumDropdown from './AlbumDropdown.vue'
import { momentFormat} from '~/utils/filters'
function groupByDisc (initial) { function groupByDisc (initial) {
function inner (acc, track) { function inner (acc, track) {
@ -276,6 +277,9 @@ export default {
AlbumDropdown AlbumDropdown
}, },
props: { id: { type: [String, Number], required: true } }, props: { id: { type: [String, Number], required: true } },
setup () {
return { momentFormat }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -61,13 +61,13 @@
</translate> </translate>
</div> </div>
<div class="value"> <div class="value">
{{ remainingSpace * 1000 * 1000 | humanSize }} {{ humanSize(remainingSpace * 1000 * 1000) }}
</div> </div>
</div> </div>
<div class="ui divider" /> <div class="ui divider" />
<h2 class="ui header"> <h2 class="ui header">
<translate translate-context="Content/Library/Title/Verb"> <translate translate-context="Content/Library/Title/Verb">
Upload music from your local storage Upload music from '~/your local storage
</translate> </translate>
</h2> </h2>
<div class="ui message"> <div class="ui message">
@ -174,9 +174,9 @@
:key="file.id" :key="file.id"
> >
<td :title="file.name"> <td :title="file.name">
{{ file.name | truncate(60) }} {{ truncate(file.name, 60) }}
</td> </td>
<td>{{ file.size | humanSize }}</td> <td>{{ humanSize(file.size) }}</td>
<td> <td>
<span <span
v-if="file.error" v-if="file.error"
@ -318,6 +318,7 @@ import FsBrowser from './FsBrowser.vue'
import FsLogs from './FsLogs.vue' import FsLogs from './FsLogs.vue'
import LibraryFilesTable from '~/views/content/libraries/FilesTable.vue' import LibraryFilesTable from '~/views/content/libraries/FilesTable.vue'
import moment from 'moment' import moment from 'moment'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -330,6 +331,9 @@ export default {
library: { type: Object, required: true }, library: { type: Object, required: true },
defaultImportReference: { type: String, required: false, default: '' } defaultImportReference: { type: String, required: false, default: '' }
}, },
setup () {
return { humanSize, truncate }
},
data () { data () {
const importReference = this.defaultImportReference || moment().format() const importReference = this.defaultImportReference || moment().format()
// Since $router.replace is pushing the same route, it raises NavigationDuplicated // Since $router.replace is pushing the same route, it raises NavigationDuplicated

View File

@ -228,7 +228,7 @@ import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import Modal from '~/components/semantic/Modal.vue' import Modal from '~/components/semantic/Modal.vue'
import EmbedWizard from '~/components/audio/EmbedWizard.vue' import EmbedWizard from '~/components/audio/EmbedWizard.vue'
import ReportMixin from '~/components/mixins/Report.vue' import ReportMixin from '~/components/mixins/Report.vue'
import { momentFormat } from '~/init/filters' import { momentFormat } from '~/utils/filters'
import updateQueryString from '~/composables/updateQueryString' import updateQueryString from '~/composables/updateQueryString'
import useLogger from '~/composables/useLogger' import useLogger from '~/composables/useLogger'

View File

@ -48,7 +48,7 @@
</td> </td>
<td class="right aligned"> <td class="right aligned">
<template v-if="upload.duration"> <template v-if="upload.duration">
{{ upload.duration | duration }} {{ time.parse(upload.duration) }}
</template> </template>
<translate <translate
v-else v-else
@ -66,7 +66,7 @@
</td> </td>
<td class="right aligned"> <td class="right aligned">
<template v-if="upload.size"> <template v-if="upload.size">
{{ upload.size | humanSize }} {{ humanSize(upload.size) }}
</template> </template>
<translate <translate
v-else v-else
@ -102,7 +102,7 @@
</td> </td>
<td class="right aligned"> <td class="right aligned">
<template v-if="upload.bitrate"> <template v-if="upload.bitrate">
{{ upload.bitrate | humanSize }}/s {{ humanSize(upload.bitrate) }}/s
</template> </template>
<translate <translate
v-else v-else
@ -186,7 +186,7 @@
</td> </td>
<td class="right aligned"> <td class="right aligned">
<template v-if="track.album && track.album.release_date"> <template v-if="track.album && track.album.release_date">
{{ track.album.release_date | moment('Y') }} {{ momentFormat(track.album.release_date, 'Y') }}
</template> </template>
<template v-else> <template v-else>
<translate translate-context="*/*/*"> <translate translate-context="*/*/*">
@ -205,7 +205,7 @@
<span <span
v-if="track.copyright" v-if="track.copyright"
:title="track.copyright" :title="track.copyright"
>{{ track.copyright|truncate(50) }}</span> >{{ truncate(track.copyright, 50) }}</span>
<template v-else> <template v-else>
<translate translate-context="*/*/*"> <translate translate-context="*/*/*">
N/A N/A
@ -246,7 +246,7 @@
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
{{ track.fid|truncate(65) }} {{ truncate(track.fid, 65) }}
</a> </a>
</td> </td>
</tr> </tr>
@ -298,6 +298,8 @@ import axios from 'axios'
import LibraryWidget from '~/components/federation/LibraryWidget.vue' import LibraryWidget from '~/components/federation/LibraryWidget.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import PlaylistWidget from '~/components/playlists/Widget.vue' import PlaylistWidget from '~/components/playlists/Widget.vue'
import { humanSize, momentFormat, truncate } from '~/utils/filters'
import time from '~/utils/time'
export default { export default {
components: { components: {
@ -309,6 +311,9 @@ export default {
track: { type: Object, required: true }, track: { type: Object, required: true },
libraries: { type: Array, default: null } libraries: { type: Array, default: null }
}, },
setup () {
return { humanSize, momentFormat, time, truncate }
},
data () { data () {
return { return {
id: this.track.id, id: this.track.id,

View File

@ -105,7 +105,7 @@
> >
<td> <td>
<router-link :to="{name: 'manage.library.tags.detail', params: {id: scope.obj.name }}"> <router-link :to="{name: 'manage.library.tags.detail', params: {id: scope.obj.name }}">
{{ scope.obj.name|truncate(30, "…", true) }} {{ truncate(scope.obj.name, 30, undefined, true) }}
</router-link> </router-link>
</td> </td>
<td> <td>
@ -156,6 +156,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue' import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
import ImportStatusModal from '~/components/library/ImportStatusModal.vue' import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
import { truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -167,6 +168,9 @@ export default {
props: { props: {
filters: { type: Object, required: false, default: () => { return {} } } filters: { type: Object, required: false, default: () => { return {} } }
}, },
setup () {
return { truncate }
},
data () { data () {
return { return {
detailedUpload: {}, detailedUpload: {},

View File

@ -196,7 +196,7 @@
:title="scope.obj.library.name" :title="scope.obj.library.name"
@click.prevent="addSearchToken('library_id', scope.obj.library.id)" @click.prevent="addSearchToken('library_id', scope.obj.library.id)"
> >
{{ scope.obj.library.name | truncate(20) }} {{ truncate(scope.obj.library.name, 20) }}
</a> </a>
</td> </td>
<td> <td>
@ -258,7 +258,7 @@
</button> </button>
</td> </td>
<td> <td>
<span v-if="scope.obj.size">{{ scope.obj.size | humanSize }}</span> <span v-if="scope.obj.size">{{ humanSize(scope.obj.size) }}</span>
<translate <translate
v-else v-else
translate-context="*/*/*" translate-context="*/*/*"
@ -317,6 +317,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue' import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
import ImportStatusModal from '~/components/library/ImportStatusModal.vue' import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -328,6 +329,9 @@ export default {
props: { props: {
filters: { type: Object, required: false, default: function () { return {} } } filters: { type: Object, required: false, default: function () { return {} } }
}, },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
detailedUpload: {}, detailedUpload: {},

View File

@ -1,23 +1,59 @@
<script setup lang="ts">
import { truncate } from '~/utils/filters'
import { computed, ref } from 'vue'
interface Props {
tags: string[]
showMore?: boolean
truncateSize?: number
limit?: number
labelClasses?: string
detailRoute?: string
}
const props = withDefaults(
defineProps<Props>(),
{
showMore: true,
truncateSize: 25,
limit: 5,
labelClasses: '',
detailRoute: 'library.tags.detail'
}
)
const honorLimit = ref(true)
const tags = computed(() => {
if (!honorLimit.value) {
return props.tags
}
return props.tags.slice(0, props.limit)
})
</script>
<template> <template>
<div class="component-tags-list"> <div class="component-tags-list">
<router-link <router-link
v-for="tag in toDisplay" v-for="tag in tags"
:key="tag" :key="tag"
:to="{name: detailRoute, params: {id: tag}}" :to="{name: props.detailRoute, params: { id: tag } }"
:class="['ui', 'circular', 'hashtag', 'label', labelClasses]" :class="['ui', 'circular', 'hashtag', 'label', props.labelClasses]"
> >
#{{ tag|truncate(truncateSize) }} #{{ truncate(tag, props.truncateSize) }}
</router-link> </router-link>
<div <div
v-if="showMore && toDisplay.length < tags.length" v-if="props.showMore && tags.length < props.tags.length"
role="button" role="button"
class="ui circular inverted accent label" class="ui circular inverted accent label"
@click.prevent="honorLimit = false" @click.prevent="honorLimit = false"
> >
<translate <translate
translate-context="Content/*/Button/Label/Verb" translate-context="Content/*/Button/Label/Verb"
:translate-params="{count: tags.length - toDisplay.length}" :translate-params="{ count: props.tags.length - tags.length }"
:translate-n="tags.length - toDisplay.length" :translate-n="props.tags.length - tags.length"
translate-plural="Show %{ count } more tags" translate-plural="Show %{ count } more tags"
> >
Show 1 more tag Show 1 more tag
@ -25,28 +61,3 @@
</div> </div>
</div> </div>
</template> </template>
<script>
export default {
props: {
tags: { type: Array, required: true },
showMore: { type: Boolean, default: true },
truncateSize: { type: Number, default: 25 },
limit: { type: Number, default: 5 },
labelClasses: { type: String, default: '' },
detailRoute: { type: String, default: 'library.tags.detail' }
},
data () {
return {
honorLimit: true
}
},
computed: {
toDisplay () {
if (!this.honorLimit) {
return this.tags
}
return (this.tags || []).slice(0, this.limit)
}
}
}
</script>

View File

@ -1,140 +0,0 @@
import { InitModule } from '~/types'
import Vue from 'vue'
import time from '~/utils/time'
import moment from 'moment'
export function truncate (str: string, max = 100, ellipsis = '…', middle = false) {
if (max === 0) {
return ''
}
if (str.length <= max) {
return str
}
if (middle) {
const sepLen = 1
const charsToShow = max - sepLen
const frontChars = Math.ceil(charsToShow / 2)
const backChars = Math.floor(charsToShow / 2)
return str.substr(0, frontChars) +
ellipsis +
str.substr(str.length - backChars)
} else {
return str.slice(0, max) + ellipsis
}
}
export function ago (date: Date, locale: string) {
locale = locale || 'en'
const m = moment(date)
m.locale(locale)
return m.calendar(null, {
sameDay: 'LT',
nextDay: 'L',
nextWeek: 'L',
lastDay: 'L',
lastWeek: 'L',
sameElse: 'L'
})
}
export function fromNow (date: Date, locale: string) {
locale = 'en'
moment.locale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: 'seconds',
ss: '%ss',
m: 'a minute',
mm: '%dm',
h: 'an hour',
hh: '%dh',
d: 'a day',
dd: '%dd',
M: 'a month',
MM: '%dM',
y: 'a year',
yy: '%dY'
}
})
const m = moment(date)
m.locale(locale)
return m.fromNow(true)
}
export function secondsToObject (seconds: number) {
const m = moment.duration(seconds, 'seconds')
return {
seconds: m.seconds(),
minutes: m.minutes(),
hours: m.hours()
}
}
export function padDuration (duration: string) {
let s = String(duration)
while (s.length < 2) { s = '0' + s }
return s
}
export function duration (seconds: string) {
return time.parse(+seconds)
}
export function momentFormat (date: Date, format: string) {
format = format || 'lll'
return moment(date).format(format)
}
export function year (date: Date) {
return moment(date).year()
}
export function capitalize (str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
export function humanSize (bytes: number) {
const si = true
const thresh = si ? 1000 : 1024
if (Math.abs(bytes) < thresh) {
return bytes + ' B'
}
const units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
let u = -1
do {
bytes /= thresh
++u
} while (Math.abs(bytes) >= thresh && u < units.length - 1)
return bytes.toFixed(1) + ' ' + units[u]
}
// Remove duplicates from a list
export function unique (list: Record<string, unknown>[], property: string) {
property = property || 'id'
const unique: Record<string, unknown>[] = []
list.map(x => unique.filter(a => a[property] === x[property]).length > 0 ? null : unique.push(x))
return unique
}
// TODO (wvffle): Migrate to Vue 3
// Replace filters with computed values
export const install: InitModule = () => {
Vue.filter('humanSize', humanSize)
Vue.filter('unique', unique)
Vue.filter('capitalize', capitalize)
Vue.filter('moment', momentFormat)
Vue.filter('year', year)
Vue.filter('duration', duration)
Vue.filter('padDuration', padDuration)
Vue.filter('secondsToObject', secondsToObject)
Vue.filter('fromNow', fromNow)
Vue.filter('ago', ago)
Vue.filter('truncate', truncate)
}

View File

@ -70,3 +70,11 @@ export interface ListenWSEvent {
} }
export type WebSocketEvent = PendingReviewEditsWSEvent | PendingReviewReportsWSEvent | PendingReviewRequestsWSEvent | ListenWSEvent export type WebSocketEvent = PendingReviewEditsWSEvent | PendingReviewReportsWSEvent | PendingReviewRequestsWSEvent | ListenWSEvent
// Yet uncategorized stuff
export interface Actor {
preferred_username: string
full_username: string
is_local: boolean
domain: string
}

View File

@ -0,0 +1,38 @@
import moment from 'moment'
export function truncate (str: string, max = 100, ellipsis = '…', middle = false) {
if (max === 0) return ''
if (str.length <= max) return str
if (!middle) return str.slice(0, max) + ellipsis
const charsToShow = max - ellipsis.length
return str.slice(0, Math.ceil(charsToShow / 2)) +
ellipsis +
str.slice(-Math.floor(charsToShow / 2))
}
export function momentFormat (date: Date, format = 'lll') {
return moment(date).format(format)
}
const HUMAN_UNITS = {
SI: ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
powerOf2: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
}
export function humanSize (bytes: number, isSI = true) {
const threshold = isSI ? 1000 : 1024
if (Math.abs(bytes) < threshold) {
return `${bytes} B`
}
const units = HUMAN_UNITS[isSI ? 'SI' : 'powerOf2']
let u = -1
do {
bytes /= threshold
++u
} while (Math.abs(bytes) >= threshold && u < units.length - 1)
return `${bytes.toFixed(1)} ${units[u]}`
}

View File

@ -26,7 +26,7 @@
src="../../assets/audio/default-cover.png" src="../../assets/audio/default-cover.png"
> >
<div class="content"> <div class="content">
{{ object.artist.name | truncate(100) }} {{ truncate(object.artist.name) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.artist.is_local"> <template v-if="object.artist.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -354,7 +354,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -364,7 +364,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -418,6 +418,7 @@ import axios from 'axios'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import FetchButton from '~/components/federation/FetchButton.vue' import FetchButton from '~/components/federation/FetchButton.vue'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -425,6 +426,9 @@ export default {
TagsList TagsList
}, },
props: { id: { type: String, required: true } }, props: { id: { type: String, required: true } },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -26,7 +26,7 @@
src="../../../assets/audio/default-cover.png" src="../../../assets/audio/default-cover.png"
> >
<div class="content"> <div class="content">
{{ object.title | truncate(100) }} {{ truncate(object.title) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.is_local"> <template v-if="object.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -337,7 +337,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -347,7 +347,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
@ -401,6 +401,7 @@
import axios from 'axios' import axios from 'axios'
import FetchButton from '~/components/federation/FetchButton.vue' import FetchButton from '~/components/federation/FetchButton.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import { humanSize, truncate} from '~/utils/filters'
export default { export default {
components: { components: {
@ -408,6 +409,9 @@ export default {
TagsList TagsList
}, },
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -26,7 +26,7 @@
src="../../../assets/audio/default-cover.png" src="../../../assets/audio/default-cover.png"
> >
<div class="content"> <div class="content">
{{ object.name | truncate(100) }} {{ truncate(object.name) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.is_local"> <template v-if="object.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -336,7 +336,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -346,7 +346,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
@ -413,6 +413,7 @@ import axios from 'axios'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import FetchButton from '~/components/federation/FetchButton.vue' import FetchButton from '~/components/federation/FetchButton.vue'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -420,6 +421,9 @@ export default {
TagsList TagsList
}, },
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -17,7 +17,7 @@
<h2 class="ui header"> <h2 class="ui header">
<i class="circular inverted book icon" /> <i class="circular inverted book icon" />
<div class="content"> <div class="content">
{{ object.name | truncate(100) }} {{ truncate(object.name) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.is_local"> <template v-if="object.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -285,7 +285,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -295,7 +295,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -360,6 +360,7 @@
import axios from 'axios' import axios from 'axios'
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import useLogger from '~/composables/useLogger' import useLogger from '~/composables/useLogger'
import { humanSize, truncate} from '~/utils/filters'
const logger = useLogger() const logger = useLogger()
@ -368,6 +369,9 @@ export default {
TranslationsMixin TranslationsMixin
], ],
props: { id: { type: String, required: true } }, props: { id: { type: String, required: true } },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -17,7 +17,7 @@
<h2 class="ui header"> <h2 class="ui header">
<i class="circular inverted hashtag icon" /> <i class="circular inverted hashtag icon" />
<div class="content"> <div class="content">
{{ object.name | truncate(100) }} {{ truncate(object.name) }}
</div> </div>
</h2> </h2>
<div class="header-buttons"> <div class="header-buttons">
@ -210,9 +210,13 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import { truncate} from '~/utils/filters'
export default { export default {
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -26,7 +26,7 @@
src="../../../assets/audio/default-cover.png" src="../../../assets/audio/default-cover.png"
> >
<div class="content"> <div class="content">
{{ object.title | truncate(100) }} {{ truncate(object.title) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.is_local"> <template v-if="object.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -402,7 +402,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -412,7 +412,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
@ -454,6 +454,7 @@
import axios from 'axios' import axios from 'axios'
import FetchButton from '~/components/federation/FetchButton.vue' import FetchButton from '~/components/federation/FetchButton.vue'
import TagsList from '~/components/tags/List.vue' import TagsList from '~/components/tags/List.vue'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -461,6 +462,9 @@ export default {
TagsList TagsList
}, },
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { humanSize, truncate }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -21,7 +21,7 @@
<h2 class="ui header"> <h2 class="ui header">
<i class="circular inverted file icon" /> <i class="circular inverted file icon" />
<div class="content"> <div class="content">
{{ displayName(object) | truncate(100) }} {{ truncate(displayName(object)) }}
<div class="sub header"> <div class="sub header">
<template v-if="object.is_local"> <template v-if="object.is_local">
<span class="ui tiny accent label"> <span class="ui tiny accent label">
@ -289,7 +289,7 @@
</td> </td>
<td> <td>
<template v-if="object.audio_file"> <template v-if="object.audio_file">
{{ object.size | humanSize }} {{ humanSize(object.size) }}
</template> </template>
<translate <translate
v-else v-else
@ -306,7 +306,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ object.size | humanSize }} {{ humanSize(object.size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -317,7 +317,7 @@
</td> </td>
<td> <td>
<template v-if="object.bitrate"> <template v-if="object.bitrate">
{{ object.bitrate | humanSize }}/s {{ humanSize(object.bitrate) }}/s
</template> </template>
<translate <translate
v-else v-else
@ -335,7 +335,7 @@
</td> </td>
<td> <td>
<template v-if="object.duration"> <template v-if="object.duration">
{{ object.duration | duration }} {{ time.parse(object.duration) }}
</template> </template>
<translate <translate
v-else v-else
@ -380,6 +380,7 @@ import axios from 'axios'
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import ImportStatusModal from '~/components/library/ImportStatusModal.vue' import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
import time from '~/utils/time' import time from '~/utils/time'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -389,9 +390,11 @@ export default {
TranslationsMixin TranslationsMixin
], ],
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { humanSize, time, truncate }
},
data () { data () {
return { return {
time,
detailedUpload: {}, detailedUpload: {},
showUploadDetailModal: false, showUploadDetailModal: false,
isLoading: true, isLoading: true,

View File

@ -447,7 +447,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr v-if="object.user"> <tr v-if="object.user">
@ -486,7 +486,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -572,6 +572,7 @@ import $ from 'jquery'
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue' import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue' import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
import useLogger from '~/composables/useLogger' import useLogger from '~/composables/useLogger'
import { humanSize} from '~/utils/filters'
const logger = useLogger() const logger = useLogger()
@ -581,6 +582,9 @@ export default {
InstancePolicyCard InstancePolicyCard
}, },
props: { id: { type: Number, required: true } }, props: { id: { type: Number, required: true } },
setup () {
return { humanSize }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -361,7 +361,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_downloaded_size | humanSize }} {{ humanSize(stats.media_downloaded_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -371,7 +371,7 @@
</translate> </translate>
</td> </td>
<td> <td>
{{ stats.media_total_size | humanSize }} {{ humanSize(stats.media_total_size) }}
</td> </td>
</tr> </tr>
<tr> <tr>
@ -462,6 +462,7 @@ import { get } from 'lodash-es'
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue' import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue' import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
import { humanSize} from '~/utils/filters'
export default { export default {
components: { components: {
@ -469,6 +470,9 @@ export default {
InstancePolicyCard InstancePolicyCard
}, },
props: { id: { type: String, required: true }, allowListEnabled: { type: Boolean, required: true } }, props: { id: { type: String, required: true }, allowListEnabled: { type: Boolean, required: true } },
setup () {
return { humanSize }
},
data () { data () {
return { return {
get, get,

View File

@ -82,7 +82,7 @@
</template> </template>
<script> <script>
import { humanSize } from '~/init/filters' import { humanSize } from '~/utils/filters'
export default { export default {
computed: { computed: {

View File

@ -42,7 +42,7 @@
:data-tooltip="size_label" :data-tooltip="size_label"
> >
<i class="database icon" /> <i class="database icon" />
{{ library.size | humanSize }} {{ humanSize(library.size) }}
</span> </span>
<i class="music icon" /> <i class="music icon" />
<translate <translate
@ -79,10 +79,14 @@
<script> <script>
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import { humanSize} from '~/utils/filters'
export default { export default {
mixins: [TranslationsMixin], mixins: [TranslationsMixin],
props: { library: { type: Object, required: true } }, props: { library: { type: Object, required: true } },
setup () {
return { humanSize }
},
computed: { computed: {
size_label () { size_label () {
return this.$pgettext('Content/Library/Card.Help text', 'Total size of the files in this library') return this.$pgettext('Content/Library/Card.Help text', 'Total size of the files in this library')

View File

@ -181,7 +181,7 @@
<template v-if="scope.obj.track"> <template v-if="scope.obj.track">
<td> <td>
<router-link :to="{name: 'library.tracks.detail', params: {id: scope.obj.track.id }}"> <router-link :to="{name: 'library.tracks.detail', params: {id: scope.obj.track.id }}">
{{ scope.obj.track.title|truncate(25) }} {{ truncate(scope.obj.track.title, 25) }}
</router-link> </router-link>
</td> </td>
<td> <td>
@ -189,7 +189,7 @@
href="" href=""
class="discrete link" class="discrete link"
@click.prevent="addSearchToken('artist', scope.obj.track.artist.name)" @click.prevent="addSearchToken('artist', scope.obj.track.artist.name)"
>{{ scope.obj.track.artist.name|truncate(20) }}</a> >{{ truncate(scope.obj.track.artist.name, 20) }}</a>
</td> </td>
<td> <td>
<a <a
@ -197,12 +197,12 @@
href="" href=""
class="discrete link" class="discrete link"
@click.prevent="addSearchToken('album', scope.obj.track.album.title)" @click.prevent="addSearchToken('album', scope.obj.track.album.title)"
>{{ scope.obj.track.album.title|truncate(20) }}</a> >{{ truncate(scope.obj.track.album.title, 20) }}</a>
</td> </td>
</template> </template>
<template v-else> <template v-else>
<td :title="scope.obj.source"> <td :title="scope.obj.source">
{{ scope.obj.source | truncate(25) }} {{ truncate(scope.obj.source, 25) }}
</td> </td>
<td /> <td />
<td /> <td />
@ -227,7 +227,7 @@
</button> </button>
</td> </td>
<td v-if="scope.obj.duration"> <td v-if="scope.obj.duration">
{{ scope.obj.duration | duration }} {{ time.parse(scope.obj.duration) }}
</td> </td>
<td v-else> <td v-else>
<translate translate-context="*/*/*"> <translate translate-context="*/*/*">
@ -235,7 +235,7 @@
</translate> </translate>
</td> </td>
<td v-if="scope.obj.size"> <td v-if="scope.obj.size">
{{ scope.obj.size | humanSize }} {{ humanSize(scope.obj.size) }}
</td> </td>
<td v-else> <td v-else>
<translate translate-context="*/*/*"> <translate translate-context="*/*/*">
@ -277,6 +277,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
import TranslationsMixin from '~/components/mixins/Translations.vue' import TranslationsMixin from '~/components/mixins/Translations.vue'
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue' import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
import ImportStatusModal from '~/components/library/ImportStatusModal.vue' import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
import { humanSize, truncate } from '~/utils/filters'
export default { export default {
components: { components: {
@ -296,9 +297,11 @@ export default {
} }
} }
}, },
setup () {
return { humanSize, time, truncate }
},
data () { data () {
return { return {
time,
detailedUpload: {}, detailedUpload: {},
showUploadDetailModal: false, showUploadDetailModal: false,
isLoading: false, isLoading: false,

View File

@ -194,7 +194,7 @@
</template> </template>
<script> <script>
import axios from 'axios' import axios from 'axios'
import { humanSize } from '~/init/filters' import { humanSize } from '~/utils/filters'
import { compileTokens } from '~/search' import { compileTokens } from '~/search'
export default { export default {

View File

@ -111,7 +111,7 @@
</translate> </translate>
<span v-if="object.size"> <span v-if="object.size">
· <i class="database icon" /> · <i class="database icon" />
{{ object.size | humanSize }} {{ humanSize(object.size) }}
</span> </span>
</p> </p>
@ -230,6 +230,7 @@ import axios from 'axios'
import LibraryFollowButton from '~/components/audio/LibraryFollowButton.vue' import LibraryFollowButton from '~/components/audio/LibraryFollowButton.vue'
import ReportMixin from '~/components/mixins/Report.vue' import ReportMixin from '~/components/mixins/Report.vue'
import RadioButton from '~/components/radios/Button.vue' import RadioButton from '~/components/radios/Button.vue'
import { humanSize } from '~/utils/filters'
export default { export default {
components: { components: {
@ -242,6 +243,9 @@ export default {
next() next()
}, },
props: { id: { type: String, required: true } }, props: { id: { type: String, required: true } },
setup () {
return { humanSize }
},
data () { data () {
return { return {
isLoading: true, isLoading: true,

View File

@ -2,7 +2,7 @@ import {expect} from 'chai'
import Username from '~/components/common/Username.vue' import Username from '~/components/common/Username.vue'
import { render } from '../../utils' import { render } from '~/utils'
describe('Username', () => { describe('Username', () => {
it('displays username', () => { it('displays username', () => {

View File

@ -1,59 +1,22 @@
import {expect} from 'chai' import { expect } from 'chai'
import moment from 'moment' import { truncate } from '~/filters'
import {truncate, ago, capitalize, year, unique} from '~/init/filters'
describe('filters', () => { describe('filters', () => {
describe('truncate', () => { describe('truncate', () => {
it('leave strings as it if correct size', () => { it('leave strings as it if correct size', () => {
const input = 'Hello world' const input = 'Hello world'
let output = truncate(input, 100) const output = truncate(input, 100)
expect(output).to.equal(input) expect(output).to.equal(input)
}) })
it('returns shorter string with character', () => { it('returns shorter string with character', () => {
const input = 'Hello world' const input = 'Hello world'
let output = truncate(input, 5) const output = truncate(input, 5)
expect(output).to.equal('Hello…') expect(output).to.equal('Hello…')
}) })
it('custom ellipsis', () => { it('custom ellipsis', () => {
const input = 'Hello world' const input = 'Hello world'
let output = truncate(input, 5, ' pouet') const output = truncate(input, 5, ' pouet')
expect(output).to.equal('Hello pouet') expect(output).to.equal('Hello pouet')
}) })
}) })
describe('ago', () => {
it('works', () => {
const input = new Date()
let output = ago(input)
let expected = moment(input).calendar(input, {
sameDay: 'LT',
nextDay: 'L',
nextWeek: 'L',
lastDay: 'L',
lastWeek: 'L',
sameElse: 'L'
})
expect(output).to.equal(expected)
})
})
describe('year', () => {
it('works', () => {
const input = '2017-07-13'
let output = year(input)
expect(output).to.equal(2017)
})
})
describe('capitalize', () => {
it('works', () => {
const input = 'hello world'
let output = capitalize(input)
expect(output).to.deep.equal('Hello world')
})
})
describe('unique', () => {
it('works', () => {
const list = [{id: 1}, {id: 2}, {id: 3}, {id: 1}]
const dedupedList = unique(list, 'id')
expect(dedupedList).to.have.deep.members([{id: 1}, {id: 3}, {id: 2}])
})
})
}) })

View File

@ -4,7 +4,7 @@ import {expect} from 'chai'
import moxios from 'moxios' import moxios from 'moxios'
import store from '~/store/auth' import store from '~/store/auth'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/auth', () => { describe('store/auth', () => {
var sandbox var sandbox

View File

@ -2,7 +2,7 @@ import {expect} from 'chai'
import store from '~/store/favorites' import store from '~/store/favorites'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/favorites', () => { describe('store/favorites', () => {
describe('mutations', () => { describe('mutations', () => {

View File

@ -3,7 +3,7 @@ var sinon = require('sinon')
import axios from 'axios' import axios from 'axios'
import moxios from 'moxios' import moxios from 'moxios'
import store from '~/store/instance' import store from '~/store/instance'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/instance', () => { describe('store/instance', () => {
var sandbox var sandbox

View File

@ -2,7 +2,7 @@ import {expect} from 'chai'
import store from '~/store/player' import store from '~/store/player'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/player', () => { describe('store/player', () => {
describe('mutations', () => { describe('mutations', () => {

View File

@ -3,7 +3,7 @@ var sinon = require('sinon')
import moxios from 'moxios' import moxios from 'moxios'
import store from '~/store/playlists' import store from '~/store/playlists'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/playlists', () => { describe('store/playlists', () => {
var sandbox var sandbox

View File

@ -3,7 +3,7 @@ import {expect} from 'chai'
import * as _ from 'lodash-es' import * as _ from 'lodash-es'
import store from '~/store/queue' import store from '~/store/queue'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/queue', () => { describe('store/queue', () => {
var sandbox var sandbox

View File

@ -3,7 +3,7 @@ import {expect} from 'chai'
import moxios from 'moxios' import moxios from 'moxios'
import store from '~/store/radios' import store from '~/store/radios'
import { testAction } from '../../utils' import { testAction } from '~/utils'
describe('store/radios', () => { describe('store/radios', () => {
var sandbox var sandbox

View File

@ -2909,11 +2909,14 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0" whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0" whatwg-url "^8.0.0"
<<<<<<< HEAD
de-indent@^1.0.2: de-indent@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
=======
>>>>>>> 25ad51a8 (Remove vue 2 filters)
deasync@^0.1.15: deasync@^0.1.15:
version "0.1.28" version "0.1.28"
resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.28.tgz#9b447b79b3f822432f0ab6a8614c0062808b5ad2" resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.28.tgz#9b447b79b3f822432f0ab6a8614c0062808b5ad2"
@ -4068,11 +4071,6 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
he@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
howler@2.2.3: howler@2.2.3:
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da" resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da"
@ -6764,14 +6762,6 @@ vue-router@4.0.14:
dependencies: dependencies:
"@vue/devtools-api" "^6.0.0" "@vue/devtools-api" "^6.0.0"
vue-template-compiler@2.6.14:
version "2.6.14"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763"
integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
vue-template-es2015-compiler@^1.6.0: vue-template-es2015-compiler@^1.6.0:
version "1.9.1" version "1.9.1"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"