parent
12b09b085a
commit
b7d66232f6
|
@ -82,7 +82,6 @@
|
|||
"vite": "2.8.6",
|
||||
"vite-plugin-pwa": "0.12.0",
|
||||
"vue-jest": "3.0.7",
|
||||
"vue-template-compiler": "2.6.14",
|
||||
"workbox-core": "6.5.3",
|
||||
"workbox-precaching": "6.5.3",
|
||||
"workbox-routing": "6.5.3",
|
||||
|
|
|
@ -252,7 +252,7 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { get } from 'lodash-es'
|
||||
import showdown from 'showdown'
|
||||
import { humanSize } from '~/init/filters'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
import SignupForm from '~/components/auth/SignupForm.vue'
|
||||
import LogoText from '~/components/LogoText.vue'
|
||||
|
|
|
@ -278,7 +278,7 @@ We render some markdown to html here, the content is set by the admin so we shou
|
|||
class="right aligned"
|
||||
>
|
||||
<span class="features-status ui text">
|
||||
{{ defaultUploadQuota * 1000 * 1000 | humanSize }}
|
||||
{{ humanSize(defaultUploadQuota * 1000 * 1000) }}
|
||||
</span>
|
||||
</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 { get } from 'lodash-es'
|
||||
import showdown from 'showdown'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// TODO (wvffle): Remove v-html
|
||||
markdown: new showdown.Converter(),
|
||||
showAllowedDomains: false
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ import AlbumWidget from '~/components/audio/album/Widget.vue'
|
|||
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
|
||||
import LoginForm from '~/components/auth/LoginForm.vue'
|
||||
import SignupForm from '~/components/auth/SignupForm.vue'
|
||||
import { humanSize } from '~/init/filters'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
|
||||
import { momentFormat } from '~/init/filters'
|
||||
import { momentFormat } from '~/utils/filters'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<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-context="*/*/*"
|
||||
:translate-params="{count: album.tracks_count}"
|
||||
|
@ -59,6 +59,7 @@
|
|||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import { momentFormat} from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -67,6 +68,9 @@ export default {
|
|||
props: {
|
||||
album: { type: Object, required: true }
|
||||
},
|
||||
setup () {
|
||||
return { momentFormat }
|
||||
},
|
||||
computed: {
|
||||
imageUrl () {
|
||||
if (this.album.cover && this.album.cover.urls.original) {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
class="discrete link"
|
||||
:to="{name: 'library.artists.detail', params: {id: artist.id}}"
|
||||
>
|
||||
{{ artist.name|truncate(30) }}
|
||||
{{ truncate(artist.name, 30) }}
|
||||
</router-link>
|
||||
</strong>
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
|||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -74,6 +75,9 @@ export default {
|
|||
TagsList
|
||||
},
|
||||
props: { artist: { type: Object, required: true } },
|
||||
setup () {
|
||||
return { truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
initialAlbums: 30,
|
||||
|
|
|
@ -158,12 +158,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { clone } from 'lodash-es'
|
||||
import { clone, uniqBy } from 'lodash-es'
|
||||
import axios from 'axios'
|
||||
import TrackRow from '~/components/audio/track/Row.vue'
|
||||
import TrackMobileRow from '~/components/audio/track/MobileRow.vue'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import { unique } from '~/init/filters'
|
||||
import { unique } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -205,7 +205,7 @@ export default {
|
|||
computed: {
|
||||
allTracks () {
|
||||
const tracks = (this.tracks || []).concat(this.additionalTracks)
|
||||
return unique(tracks, 'id')
|
||||
return uniqBy(tracks, 'id')
|
||||
},
|
||||
|
||||
labels () {
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
</template>
|
||||
<div class="sub header">
|
||||
<template v-if="file.response.uuid">
|
||||
{{ file.size | humanSize }}
|
||||
{{ humanSize(file.size) }}
|
||||
<template v-if="file.response.duration">
|
||||
· <human-duration :duration="file.response.duration" />
|
||||
</template>
|
||||
|
@ -164,7 +164,7 @@
|
|||
>
|
||||
Pending
|
||||
</translate>
|
||||
· {{ file.size | humanSize }}
|
||||
· {{ humanSize(file.size) }}
|
||||
· {{ parseInt(file.progress) }}%
|
||||
</template>
|
||||
· <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 FileUploadWidget from '~/components/library/FileUploadWidget.vue'
|
||||
import UploadMetadataForm from '~/components/channels/UploadMetadataForm.vue'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
function setIfEmpty (obj, k, v) {
|
||||
if (obj[k] !== undefined) {
|
||||
|
@ -261,6 +262,9 @@ export default {
|
|||
props: {
|
||||
channel: { type: Object, default: null, required: false }
|
||||
},
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
availableChannels: {
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<translate translate-context="Content/Library/Paragraph">
|
||||
Remaining storage space:
|
||||
</translate>
|
||||
{{ (statusData.quotaStatus.remaining * 1000 * 1000) - statusData.uploadedSize | humanSize }}
|
||||
{{ (statusData.quotaStatus.remaining * 1000 * 1000) - humanSize(statusData.uploadedSize) }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="ui hidden clearing divider mobile-only" />
|
||||
|
@ -144,7 +144,7 @@
|
|||
<script>
|
||||
import Modal from '~/components/semantic/Modal.vue'
|
||||
import ChannelUploadForm from '~/components/channels/UploadForm.vue'
|
||||
import { humanSize } from '~/init/filters'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import {onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router'
|
||||
|
||||
export default {
|
||||
|
@ -159,6 +159,8 @@ export default {
|
|||
|
||||
onBeforeRouteUpdate(guard)
|
||||
onBeforeRouteLeave(guard)
|
||||
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -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>
|
||||
<router-link
|
||||
:to="url"
|
||||
:title="actor.full_username"
|
||||
>
|
||||
<template v-if="avatar">
|
||||
<actor-avatar :actor="actor" /><span> </span>
|
||||
</template><slot>{{ repr | truncate(truncateLength) }}</slot>
|
||||
<actor-avatar
|
||||
v-if="avatar"
|
||||
:actor="actor"
|
||||
/>
|
||||
<span> </span>
|
||||
<slot>{{ repr }}</slot>
|
||||
</router-link>
|
||||
</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>
|
||||
|
|
|
@ -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>
|
||||
<span>
|
||||
<translate
|
||||
v-if="durationData.hours > 0"
|
||||
v-if="duration.hours > 0"
|
||||
translate-context="Content/*/Paragraph"
|
||||
:translate-params="{minutes: durationData.minutes, hours: durationData.hours}"
|
||||
:translate-params="duration"
|
||||
>%{ hours } h %{ minutes } min</translate>
|
||||
<translate
|
||||
v-else
|
||||
translate-context="Content/*/Paragraph"
|
||||
:translate-params="{minutes: durationData.minutes}"
|
||||
:translate-params="duration"
|
||||
>%{ minutes } min</translate>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import { secondsToObject } from '~/init/filters'
|
||||
|
||||
export default {
|
||||
props: { seconds: { type: Number, default: null } },
|
||||
computed: {
|
||||
durationData () {
|
||||
return secondsToObject(this.seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -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>
|
||||
<time
|
||||
:datetime="date"
|
||||
:title="date | moment"
|
||||
:title="momentFormat(date)"
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
v-if="props.icon"
|
||||
class="outline clock icon"
|
||||
/>
|
||||
{{ realDate | ago($store.state.ui.momentLocale) }}
|
||||
{{ realDate }}
|
||||
</time>
|
||||
</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>
|
||||
|
|
|
@ -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>
|
||||
<time :datetime="`${duration}s`">
|
||||
{{ duration | duration }}
|
||||
{{ parsedDuration }}
|
||||
</time>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
duration: { type: Number, required: true }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
v-if="object.release_date || (totalTracks > 0)"
|
||||
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">
|
||||
<translate
|
||||
v-if="isSerie"
|
||||
|
@ -254,6 +254,7 @@ import PlayButton from '~/components/audio/PlayButton.vue'
|
|||
import TagsList from '~/components/tags/List.vue'
|
||||
import ArtistLabel from '~/components/audio/ArtistLabel.vue'
|
||||
import AlbumDropdown from './AlbumDropdown.vue'
|
||||
import { momentFormat} from '~/utils/filters'
|
||||
|
||||
function groupByDisc (initial) {
|
||||
function inner (acc, track) {
|
||||
|
@ -276,6 +277,9 @@ export default {
|
|||
AlbumDropdown
|
||||
},
|
||||
props: { id: { type: [String, Number], required: true } },
|
||||
setup () {
|
||||
return { momentFormat }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -61,13 +61,13 @@
|
|||
</translate>
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ remainingSpace * 1000 * 1000 | humanSize }}
|
||||
{{ humanSize(remainingSpace * 1000 * 1000) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider" />
|
||||
<h2 class="ui header">
|
||||
<translate translate-context="Content/Library/Title/Verb">
|
||||
Upload music from your local storage
|
||||
Upload music from '~/your local storage
|
||||
</translate>
|
||||
</h2>
|
||||
<div class="ui message">
|
||||
|
@ -174,9 +174,9 @@
|
|||
:key="file.id"
|
||||
>
|
||||
<td :title="file.name">
|
||||
{{ file.name | truncate(60) }}
|
||||
{{ truncate(file.name, 60) }}
|
||||
</td>
|
||||
<td>{{ file.size | humanSize }}</td>
|
||||
<td>{{ humanSize(file.size) }}</td>
|
||||
<td>
|
||||
<span
|
||||
v-if="file.error"
|
||||
|
@ -318,6 +318,7 @@ import FsBrowser from './FsBrowser.vue'
|
|||
import FsLogs from './FsLogs.vue'
|
||||
import LibraryFilesTable from '~/views/content/libraries/FilesTable.vue'
|
||||
import moment from 'moment'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -330,6 +331,9 @@ export default {
|
|||
library: { type: Object, required: true },
|
||||
defaultImportReference: { type: String, required: false, default: '' }
|
||||
},
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
const importReference = this.defaultImportReference || moment().format()
|
||||
// Since $router.replace is pushing the same route, it raises NavigationDuplicated
|
||||
|
|
|
@ -228,7 +228,7 @@ import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
|||
import Modal from '~/components/semantic/Modal.vue'
|
||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||
import ReportMixin from '~/components/mixins/Report.vue'
|
||||
import { momentFormat } from '~/init/filters'
|
||||
import { momentFormat } from '~/utils/filters'
|
||||
import updateQueryString from '~/composables/updateQueryString'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
</td>
|
||||
<td class="right aligned">
|
||||
<template v-if="upload.duration">
|
||||
{{ upload.duration | duration }}
|
||||
{{ time.parse(upload.duration) }}
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -66,7 +66,7 @@
|
|||
</td>
|
||||
<td class="right aligned">
|
||||
<template v-if="upload.size">
|
||||
{{ upload.size | humanSize }}
|
||||
{{ humanSize(upload.size) }}
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -102,7 +102,7 @@
|
|||
</td>
|
||||
<td class="right aligned">
|
||||
<template v-if="upload.bitrate">
|
||||
{{ upload.bitrate | humanSize }}/s
|
||||
{{ humanSize(upload.bitrate) }}/s
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -186,7 +186,7 @@
|
|||
</td>
|
||||
<td class="right aligned">
|
||||
<template v-if="track.album && track.album.release_date">
|
||||
{{ track.album.release_date | moment('Y') }}
|
||||
{{ momentFormat(track.album.release_date, 'Y') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<translate translate-context="*/*/*">
|
||||
|
@ -205,7 +205,7 @@
|
|||
<span
|
||||
v-if="track.copyright"
|
||||
:title="track.copyright"
|
||||
>{{ track.copyright|truncate(50) }}</span>
|
||||
>{{ truncate(track.copyright, 50) }}</span>
|
||||
<template v-else>
|
||||
<translate translate-context="*/*/*">
|
||||
N/A
|
||||
|
@ -246,7 +246,7 @@
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{ track.fid|truncate(65) }}
|
||||
{{ truncate(track.fid, 65) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -298,6 +298,8 @@ import axios from 'axios'
|
|||
import LibraryWidget from '~/components/federation/LibraryWidget.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import PlaylistWidget from '~/components/playlists/Widget.vue'
|
||||
import { humanSize, momentFormat, truncate } from '~/utils/filters'
|
||||
import time from '~/utils/time'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -309,6 +311,9 @@ export default {
|
|||
track: { type: Object, required: true },
|
||||
libraries: { type: Array, default: null }
|
||||
},
|
||||
setup () {
|
||||
return { humanSize, momentFormat, time, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
id: this.track.id,
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -156,6 +156,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
|
|||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -167,6 +168,9 @@ export default {
|
|||
props: {
|
||||
filters: { type: Object, required: false, default: () => { return {} } }
|
||||
},
|
||||
setup () {
|
||||
return { truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
detailedUpload: {},
|
||||
|
|
|
@ -196,7 +196,7 @@
|
|||
:title="scope.obj.library.name"
|
||||
@click.prevent="addSearchToken('library_id', scope.obj.library.id)"
|
||||
>
|
||||
{{ scope.obj.library.name | truncate(20) }}
|
||||
{{ truncate(scope.obj.library.name, 20) }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -258,7 +258,7 @@
|
|||
</button>
|
||||
</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
|
||||
v-else
|
||||
translate-context="*/*/*"
|
||||
|
@ -317,6 +317,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
|
|||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -328,6 +329,9 @@ export default {
|
|||
props: {
|
||||
filters: { type: Object, required: false, default: function () { return {} } }
|
||||
},
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
detailedUpload: {},
|
||||
|
|
|
@ -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>
|
||||
<div class="component-tags-list">
|
||||
<router-link
|
||||
v-for="tag in toDisplay"
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
:to="{name: detailRoute, params: {id: tag}}"
|
||||
:class="['ui', 'circular', 'hashtag', 'label', labelClasses]"
|
||||
:to="{name: props.detailRoute, params: { id: tag } }"
|
||||
:class="['ui', 'circular', 'hashtag', 'label', props.labelClasses]"
|
||||
>
|
||||
#{{ tag|truncate(truncateSize) }}
|
||||
#{{ truncate(tag, props.truncateSize) }}
|
||||
</router-link>
|
||||
<div
|
||||
v-if="showMore && toDisplay.length < tags.length"
|
||||
v-if="props.showMore && tags.length < props.tags.length"
|
||||
role="button"
|
||||
class="ui circular inverted accent label"
|
||||
@click.prevent="honorLimit = false"
|
||||
>
|
||||
<translate
|
||||
translate-context="Content/*/Button/Label/Verb"
|
||||
:translate-params="{count: tags.length - toDisplay.length}"
|
||||
:translate-n="tags.length - toDisplay.length"
|
||||
:translate-params="{ count: props.tags.length - tags.length }"
|
||||
:translate-n="props.tags.length - tags.length"
|
||||
translate-plural="Show %{ count } more tags"
|
||||
>
|
||||
Show 1 more tag
|
||||
|
@ -25,28 +61,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -70,3 +70,11 @@ export interface 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
|
||||
}
|
||||
|
|
|
@ -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]}`
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
src="../../assets/audio/default-cover.png"
|
||||
>
|
||||
<div class="content">
|
||||
{{ object.artist.name | truncate(100) }}
|
||||
{{ truncate(object.artist.name) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.artist.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -354,7 +354,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -364,7 +364,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -418,6 +418,7 @@ import axios from 'axios'
|
|||
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import FetchButton from '~/components/federation/FetchButton.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -425,6 +426,9 @@ export default {
|
|||
TagsList
|
||||
},
|
||||
props: { id: { type: String, required: true } },
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
src="../../../assets/audio/default-cover.png"
|
||||
>
|
||||
<div class="content">
|
||||
{{ object.title | truncate(100) }}
|
||||
{{ truncate(object.title) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -337,7 +337,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -347,7 +347,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -401,6 +401,7 @@
|
|||
import axios from 'axios'
|
||||
import FetchButton from '~/components/federation/FetchButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { humanSize, truncate} from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -408,6 +409,9 @@ export default {
|
|||
TagsList
|
||||
},
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
src="../../../assets/audio/default-cover.png"
|
||||
>
|
||||
<div class="content">
|
||||
{{ object.name | truncate(100) }}
|
||||
{{ truncate(object.name) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -336,7 +336,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -346,7 +346,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -413,6 +413,7 @@ import axios from 'axios'
|
|||
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import FetchButton from '~/components/federation/FetchButton.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -420,6 +421,9 @@ export default {
|
|||
TagsList
|
||||
},
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<h2 class="ui header">
|
||||
<i class="circular inverted book icon" />
|
||||
<div class="content">
|
||||
{{ object.name | truncate(100) }}
|
||||
{{ truncate(object.name) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -285,7 +285,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -295,7 +295,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -360,6 +360,7 @@
|
|||
import axios from 'axios'
|
||||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import { humanSize, truncate} from '~/utils/filters'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
@ -368,6 +369,9 @@ export default {
|
|||
TranslationsMixin
|
||||
],
|
||||
props: { id: { type: String, required: true } },
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<h2 class="ui header">
|
||||
<i class="circular inverted hashtag icon" />
|
||||
<div class="content">
|
||||
{{ object.name | truncate(100) }}
|
||||
{{ truncate(object.name) }}
|
||||
</div>
|
||||
</h2>
|
||||
<div class="header-buttons">
|
||||
|
@ -210,9 +210,13 @@
|
|||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { truncate} from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
src="../../../assets/audio/default-cover.png"
|
||||
>
|
||||
<div class="content">
|
||||
{{ object.title | truncate(100) }}
|
||||
{{ truncate(object.title) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -402,7 +402,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -412,7 +412,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -454,6 +454,7 @@
|
|||
import axios from 'axios'
|
||||
import FetchButton from '~/components/federation/FetchButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -461,6 +462,9 @@ export default {
|
|||
TagsList
|
||||
},
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { humanSize, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<h2 class="ui header">
|
||||
<i class="circular inverted file icon" />
|
||||
<div class="content">
|
||||
{{ displayName(object) | truncate(100) }}
|
||||
{{ truncate(displayName(object)) }}
|
||||
<div class="sub header">
|
||||
<template v-if="object.is_local">
|
||||
<span class="ui tiny accent label">
|
||||
|
@ -289,7 +289,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<template v-if="object.audio_file">
|
||||
{{ object.size | humanSize }}
|
||||
{{ humanSize(object.size) }}
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -306,7 +306,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ object.size | humanSize }}
|
||||
{{ humanSize(object.size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -317,7 +317,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<template v-if="object.bitrate">
|
||||
{{ object.bitrate | humanSize }}/s
|
||||
{{ humanSize(object.bitrate) }}/s
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -335,7 +335,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<template v-if="object.duration">
|
||||
{{ object.duration | duration }}
|
||||
{{ time.parse(object.duration) }}
|
||||
</template>
|
||||
<translate
|
||||
v-else
|
||||
|
@ -380,6 +380,7 @@ import axios from 'axios'
|
|||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import time from '~/utils/time'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -389,9 +390,11 @@ export default {
|
|||
TranslationsMixin
|
||||
],
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { humanSize, time, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
time,
|
||||
detailedUpload: {},
|
||||
showUploadDetailModal: false,
|
||||
isLoading: true,
|
||||
|
|
|
@ -447,7 +447,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="object.user">
|
||||
|
@ -486,7 +486,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -572,6 +572,7 @@ import $ from 'jquery'
|
|||
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
|
||||
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import { humanSize} from '~/utils/filters'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
@ -581,6 +582,9 @@ export default {
|
|||
InstancePolicyCard
|
||||
},
|
||||
props: { id: { type: Number, required: true } },
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -361,7 +361,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_downloaded_size | humanSize }}
|
||||
{{ humanSize(stats.media_downloaded_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -371,7 +371,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ stats.media_total_size | humanSize }}
|
||||
{{ humanSize(stats.media_total_size) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -462,6 +462,7 @@ import { get } from 'lodash-es'
|
|||
|
||||
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
|
||||
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
|
||||
import { humanSize} from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -469,6 +470,9 @@ export default {
|
|||
InstancePolicyCard
|
||||
},
|
||||
props: { id: { type: String, required: true }, allowListEnabled: { type: Boolean, required: true } },
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
get,
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { humanSize } from '~/init/filters'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
:data-tooltip="size_label"
|
||||
>
|
||||
<i class="database icon" />
|
||||
{{ library.size | humanSize }}
|
||||
{{ humanSize(library.size) }}
|
||||
</span>
|
||||
<i class="music icon" />
|
||||
<translate
|
||||
|
@ -79,10 +79,14 @@
|
|||
|
||||
<script>
|
||||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import { humanSize} from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
mixins: [TranslationsMixin],
|
||||
props: { library: { type: Object, required: true } },
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
computed: {
|
||||
size_label () {
|
||||
return this.$pgettext('Content/Library/Card.Help text', 'Total size of the files in this library')
|
||||
|
|
|
@ -181,7 +181,7 @@
|
|||
<template v-if="scope.obj.track">
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -189,7 +189,7 @@
|
|||
href=""
|
||||
class="discrete link"
|
||||
@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>
|
||||
<a
|
||||
|
@ -197,12 +197,12 @@
|
|||
href=""
|
||||
class="discrete link"
|
||||
@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>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td :title="scope.obj.source">
|
||||
{{ scope.obj.source | truncate(25) }}
|
||||
{{ truncate(scope.obj.source, 25) }}
|
||||
</td>
|
||||
<td />
|
||||
<td />
|
||||
|
@ -227,7 +227,7 @@
|
|||
</button>
|
||||
</td>
|
||||
<td v-if="scope.obj.duration">
|
||||
{{ scope.obj.duration | duration }}
|
||||
{{ time.parse(scope.obj.duration) }}
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate translate-context="*/*/*">
|
||||
|
@ -235,7 +235,7 @@
|
|||
</translate>
|
||||
</td>
|
||||
<td v-if="scope.obj.size">
|
||||
{{ scope.obj.size | humanSize }}
|
||||
{{ humanSize(scope.obj.size) }}
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate translate-context="*/*/*">
|
||||
|
@ -277,6 +277,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
|
|||
import TranslationsMixin from '~/components/mixins/Translations.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -296,9 +297,11 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
return { humanSize, time, truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
time,
|
||||
detailedUpload: {},
|
||||
showUploadDetailModal: false,
|
||||
isLoading: false,
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
</template>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { humanSize } from '~/init/filters'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import { compileTokens } from '~/search'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
</translate>
|
||||
<span v-if="object.size">
|
||||
· <i class="database icon" />
|
||||
{{ object.size | humanSize }}
|
||||
{{ humanSize(object.size) }}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
|
@ -230,6 +230,7 @@ import axios from 'axios'
|
|||
import LibraryFollowButton from '~/components/audio/LibraryFollowButton.vue'
|
||||
import ReportMixin from '~/components/mixins/Report.vue'
|
||||
import RadioButton from '~/components/radios/Button.vue'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -242,6 +243,9 @@ export default {
|
|||
next()
|
||||
},
|
||||
props: { id: { type: String, required: true } },
|
||||
setup () {
|
||||
return { humanSize }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import {expect} from 'chai'
|
|||
|
||||
import Username from '~/components/common/Username.vue'
|
||||
|
||||
import { render } from '../../utils'
|
||||
import { render } from '~/utils'
|
||||
|
||||
describe('Username', () => {
|
||||
it('displays username', () => {
|
||||
|
|
|
@ -1,59 +1,22 @@
|
|||
import {expect} from 'chai'
|
||||
import moment from 'moment'
|
||||
import {truncate, ago, capitalize, year, unique} from '~/init/filters'
|
||||
import { expect } from 'chai'
|
||||
import { truncate } from '~/filters'
|
||||
|
||||
describe('filters', () => {
|
||||
describe('truncate', () => {
|
||||
it('leave strings as it if correct size', () => {
|
||||
const input = 'Hello world'
|
||||
let output = truncate(input, 100)
|
||||
const output = truncate(input, 100)
|
||||
expect(output).to.equal(input)
|
||||
})
|
||||
it('returns shorter string with character', () => {
|
||||
const input = 'Hello world'
|
||||
let output = truncate(input, 5)
|
||||
const output = truncate(input, 5)
|
||||
expect(output).to.equal('Hello…')
|
||||
})
|
||||
it('custom ellipsis', () => {
|
||||
const input = 'Hello world'
|
||||
let output = truncate(input, 5, ' pouet')
|
||||
const output = truncate(input, 5, ' 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}])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ import {expect} from 'chai'
|
|||
import moxios from 'moxios'
|
||||
import store from '~/store/auth'
|
||||
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/auth', () => {
|
||||
var sandbox
|
||||
|
|
|
@ -2,7 +2,7 @@ import {expect} from 'chai'
|
|||
|
||||
import store from '~/store/favorites'
|
||||
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/favorites', () => {
|
||||
describe('mutations', () => {
|
||||
|
|
|
@ -3,7 +3,7 @@ var sinon = require('sinon')
|
|||
import axios from 'axios'
|
||||
import moxios from 'moxios'
|
||||
import store from '~/store/instance'
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/instance', () => {
|
||||
var sandbox
|
||||
|
|
|
@ -2,7 +2,7 @@ import {expect} from 'chai'
|
|||
|
||||
import store from '~/store/player'
|
||||
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/player', () => {
|
||||
describe('mutations', () => {
|
||||
|
|
|
@ -3,7 +3,7 @@ var sinon = require('sinon')
|
|||
import moxios from 'moxios'
|
||||
import store from '~/store/playlists'
|
||||
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/playlists', () => {
|
||||
var sandbox
|
||||
|
|
|
@ -3,7 +3,7 @@ import {expect} from 'chai'
|
|||
import * as _ from 'lodash-es'
|
||||
|
||||
import store from '~/store/queue'
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/queue', () => {
|
||||
var sandbox
|
||||
|
|
|
@ -3,7 +3,7 @@ import {expect} from 'chai'
|
|||
|
||||
import moxios from 'moxios'
|
||||
import store from '~/store/radios'
|
||||
import { testAction } from '../../utils'
|
||||
import { testAction } from '~/utils'
|
||||
|
||||
describe('store/radios', () => {
|
||||
var sandbox
|
||||
|
|
|
@ -2909,11 +2909,14 @@ data-urls@^2.0.0:
|
|||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
<<<<<<< HEAD
|
||||
de-indent@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
|
||||
|
||||
=======
|
||||
>>>>>>> 25ad51a8 (Remove vue 2 filters)
|
||||
deasync@^0.1.15:
|
||||
version "0.1.28"
|
||||
resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.28.tgz#9b447b79b3f822432f0ab6a8614c0062808b5ad2"
|
||||
|
@ -4068,11 +4071,6 @@ has@^1.0.3:
|
|||
dependencies:
|
||||
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:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da"
|
||||
|
@ -6764,14 +6762,6 @@ vue-router@4.0.14:
|
|||
dependencies:
|
||||
"@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:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||
|
|
Loading…
Reference in New Issue