diff --git a/front/package.json b/front/package.json
index 44d3112ec..34231d27b 100644
--- a/front/package.json
+++ b/front/package.json
@@ -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",
diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index ece6baf24..d954a9e2e 100644
--- a/front/src/components/About.vue
+++ b/front/src/components/About.vue
@@ -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'
diff --git a/front/src/components/AboutPod.vue b/front/src/components/AboutPod.vue
index f94308cae..4c3d0dfdd 100644
--- a/front/src/components/AboutPod.vue
+++ b/front/src/components/AboutPod.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"
>
- {{ defaultUploadQuota * 1000 * 1000 | humanSize }}
+ {{ humanSize(defaultUploadQuota * 1000 * 1000) }}
- {{ remainingSpace * 1000 * 1000 | humanSize }}
+ {{ humanSize(remainingSpace * 1000 * 1000) }}
@@ -174,9 +174,9 @@
:key="file.id"
>
- {{ file.name | truncate(60) }}
+ {{ truncate(file.name, 60) }}
|
- {{ file.size | humanSize }} |
+ {{ humanSize(file.size) }} |
|
- {{ upload.duration | duration }}
+ {{ time.parse(upload.duration) }}
- {{ upload.size | humanSize }}
+ {{ humanSize(upload.size) }}
- {{ upload.bitrate | humanSize }}/s
+ {{ humanSize(upload.bitrate) }}/s
- {{ track.album.release_date | moment('Y') }}
+ {{ momentFormat(track.album.release_date, 'Y') }}
@@ -205,7 +205,7 @@
{{ track.copyright|truncate(50) }}
+ >{{ truncate(track.copyright, 50) }}
N/A
@@ -246,7 +246,7 @@
target="_blank"
rel="noopener noreferrer"
>
- {{ track.fid|truncate(65) }}
+ {{ truncate(track.fid, 65) }}
|
@@ -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,
diff --git a/front/src/components/manage/library/TagsTable.vue b/front/src/components/manage/library/TagsTable.vue
index bed13fb7c..9246feb71 100644
--- a/front/src/components/manage/library/TagsTable.vue
+++ b/front/src/components/manage/library/TagsTable.vue
@@ -105,7 +105,7 @@
>
- {{ scope.obj.name|truncate(30, "…", true) }}
+ {{ truncate(scope.obj.name, 30, undefined, true) }}
|
@@ -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: {},
diff --git a/front/src/components/manage/library/UploadsTable.vue b/front/src/components/manage/library/UploadsTable.vue
index 9fb52d32d..9196841b2 100644
--- a/front/src/components/manage/library/UploadsTable.vue
+++ b/front/src/components/manage/library/UploadsTable.vue
@@ -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) }}
|
@@ -258,7 +258,7 @@
|
- {{ scope.obj.size | humanSize }}
+ {{ humanSize(scope.obj.size) }}
+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(),
+ {
+ 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)
+})
+
+
+
-
diff --git a/front/src/init/filters.ts b/front/src/init/filters.ts
deleted file mode 100644
index e9382f094..000000000
--- a/front/src/init/filters.ts
+++ /dev/null
@@ -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[], property: string) {
- property = property || 'id'
- const unique: Record[] = []
- 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)
-}
diff --git a/front/src/types.ts b/front/src/types.ts
index 6f0dde11c..d5e2a4259 100644
--- a/front/src/types.ts
+++ b/front/src/types.ts
@@ -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
+}
diff --git a/front/src/utils/filters.ts b/front/src/utils/filters.ts
new file mode 100644
index 000000000..cf3fb8957
--- /dev/null
+++ b/front/src/utils/filters.ts
@@ -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]}`
+}
diff --git a/front/src/views/admin/ChannelDetail.vue b/front/src/views/admin/ChannelDetail.vue
index 03a7c253c..b96c8d546 100644
--- a/front/src/views/admin/ChannelDetail.vue
+++ b/front/src/views/admin/ChannelDetail.vue
@@ -26,7 +26,7 @@
src="../../assets/audio/default-cover.png"
>
- {{ object.artist.name | truncate(100) }}
+ {{ truncate(object.artist.name) }}
|
- {{ stats.media_downloaded_size | humanSize }}
+ {{ humanSize(stats.media_downloaded_size) }}
|
@@ -364,7 +364,7 @@
- {{ stats.media_total_size | humanSize }}
+ {{ humanSize(stats.media_total_size) }}
|
@@ -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,
diff --git a/front/src/views/admin/library/AlbumDetail.vue b/front/src/views/admin/library/AlbumDetail.vue
index b17eb37bf..9d2e81087 100644
--- a/front/src/views/admin/library/AlbumDetail.vue
+++ b/front/src/views/admin/library/AlbumDetail.vue
@@ -26,7 +26,7 @@
src="../../../assets/audio/default-cover.png"
>
- {{ object.title | truncate(100) }}
+ {{ truncate(object.title) }}
@@ -347,7 +347,7 @@
- {{ stats.media_total_size | humanSize }}
+ {{ humanSize(stats.media_total_size) }}
|
@@ -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,
diff --git a/front/src/views/admin/library/ArtistDetail.vue b/front/src/views/admin/library/ArtistDetail.vue
index 6220a0bee..af4fad31d 100644
--- a/front/src/views/admin/library/ArtistDetail.vue
+++ b/front/src/views/admin/library/ArtistDetail.vue
@@ -26,7 +26,7 @@
src="../../../assets/audio/default-cover.png"
>
- {{ object.name | truncate(100) }}
+ {{ truncate(object.name) }}
|
- {{ stats.media_downloaded_size | humanSize }}
+ {{ humanSize(stats.media_downloaded_size) }}
|
@@ -346,7 +346,7 @@
- {{ stats.media_total_size | humanSize }}
+ {{ humanSize(stats.media_total_size) }}
|
@@ -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,
diff --git a/front/src/views/admin/library/LibraryDetail.vue b/front/src/views/admin/library/LibraryDetail.vue
index a69e1814d..4f582046b 100644
--- a/front/src/views/admin/library/LibraryDetail.vue
+++ b/front/src/views/admin/library/LibraryDetail.vue
@@ -17,7 +17,7 @@
|
- {{ stats.media_downloaded_size | humanSize }}
+ {{ humanSize(stats.media_downloaded_size) }}
|
@@ -295,7 +295,7 @@
- {{ stats.media_total_size | humanSize }}
+ {{ humanSize(stats.media_total_size) }}
|
@@ -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,
diff --git a/front/src/views/admin/library/TagDetail.vue b/front/src/views/admin/library/TagDetail.vue
index b6becc046..d99e671c5 100644
--- a/front/src/views/admin/library/TagDetail.vue
+++ b/front/src/views/admin/library/TagDetail.vue
@@ -17,7 +17,7 @@
| |