fix(front): [WIP] use generated types to make the CI (`lint:tsc`) happy
This commit is contained in:
parent
2b3831d4d3
commit
a5098c0952
|
@ -133,5 +133,6 @@
|
||||||
"workbox-precaching": "6.5.4",
|
"workbox-precaching": "6.5.4",
|
||||||
"workbox-routing": "6.5.4",
|
"workbox-routing": "6.5.4",
|
||||||
"workbox-strategies": "6.5.4"
|
"workbox-strategies": "6.5.4"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import { useStore } from '~/store'
|
||||||
import { get } from 'lodash-es'
|
import { get } from 'lodash-es'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import type { components } from '~/generated/types.ts'
|
|
||||||
|
|
||||||
import useMarkdown from '~/composables/useMarkdown'
|
import useMarkdown from '~/composables/useMarkdown'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
@ -40,24 +38,14 @@ const federationEnabled = computed(() => {
|
||||||
|
|
||||||
const onDesktop = computed(() => window.innerWidth > 800)
|
const onDesktop = computed(() => window.innerWidth > 800)
|
||||||
|
|
||||||
const stats = computed(() => {
|
const stats = computed(() => ({
|
||||||
const info = nodeinfo.value ?? {} as components['schemas']['NodeInfo20']
|
users: nodeinfo.value?.usage.users.activeMonth,
|
||||||
|
hours: nodeinfo.value?.metadata.content.local.hoursOfContent,
|
||||||
const data = {
|
artists: nodeinfo.value?.metadata.content.local.artists,
|
||||||
users: get(info, 'usage.users.activeMonth', null),
|
albums: nodeinfo.value?.metadata.content.local.releases, // TODO: Check where to get 'metadata.content.local.albums.total'
|
||||||
hours: get(info, 'metadata.content.local.hoursOfContent', null),
|
tracks: nodeinfo.value?.metadata.content.local.recordings, // TODO: 'metadata.content.local.tracks.total'
|
||||||
artists: get(info, 'metadata.content.local.artists.total', null),
|
listenings: nodeinfo.value?.metadata.usage?.listenings.total
|
||||||
albums: get(info, 'metadata.content.local.albums.total', null),
|
}))
|
||||||
tracks: get(info, 'metadata.content.local.tracks.total', null),
|
|
||||||
listenings: get(info, 'metadata.usage.listenings.total', null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.users === null || data.artists === null) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
})
|
|
||||||
|
|
||||||
const headerStyle = computed(() => {
|
const headerStyle = computed(() => {
|
||||||
if (!banner.value) {
|
if (!banner.value) {
|
||||||
|
@ -341,7 +329,7 @@ const headerStyle = computed(() => {
|
||||||
class="statistics-statistic"
|
class="statistics-statistic"
|
||||||
>
|
>
|
||||||
<span class="statistics-figure ui text">
|
<span class="statistics-figure ui text">
|
||||||
<span class="ui big text"><strong>{{ stats.hours.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
<span class="ui big text"><strong>{{ stats.hours?.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
||||||
<br>
|
<br>
|
||||||
{{ t('components.AboutPod.stat.hoursOfMusic', stats.hours) }}
|
{{ t('components.AboutPod.stat.hoursOfMusic', stats.hours) }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -351,7 +339,7 @@ const headerStyle = computed(() => {
|
||||||
class="statistics-statistic"
|
class="statistics-statistic"
|
||||||
>
|
>
|
||||||
<span class="statistics-figure ui text">
|
<span class="statistics-figure ui text">
|
||||||
<span class="ui big text"><strong>{{ stats.artists.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
<span class="ui big text"><strong>{{ stats.artists?.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
||||||
<br>
|
<br>
|
||||||
{{ t('components.AboutPod.stat.artistsCount', stats.artists) }}
|
{{ t('components.AboutPod.stat.artistsCount', stats.artists) }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -361,7 +349,7 @@ const headerStyle = computed(() => {
|
||||||
class="statistics-statistic"
|
class="statistics-statistic"
|
||||||
>
|
>
|
||||||
<span class="statistics-figure ui text">
|
<span class="statistics-figure ui text">
|
||||||
<span class="ui big text"><strong>{{ stats.albums.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
<span class="ui big text"><strong>{{ stats.albums?.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
||||||
<br>
|
<br>
|
||||||
{{ t('components.AboutPod.stat.albumsCount', stats.albums) }}
|
{{ t('components.AboutPod.stat.albumsCount', stats.albums) }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -371,7 +359,7 @@ const headerStyle = computed(() => {
|
||||||
class="statistics-statistic"
|
class="statistics-statistic"
|
||||||
>
|
>
|
||||||
<span class="statistics-figure ui text">
|
<span class="statistics-figure ui text">
|
||||||
<span class="ui big text"><strong>{{ stats.tracks.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
<span class="ui big text"><strong>{{ stats.tracks?.toLocaleString(store.state.ui.momentLocale) }}</strong></span>
|
||||||
<br>
|
<br>
|
||||||
{{ t('components.AboutPod.stat.tracksCount', stats.tracks) }}
|
{{ t('components.AboutPod.stat.tracksCount', stats.tracks) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -107,6 +107,8 @@ const save = async () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- TODO: type the different values in `settings` (use generics) -->
|
||||||
|
<!-- eslint-disable vue/valid-v-model -->
|
||||||
<Section
|
<Section
|
||||||
align-left
|
align-left
|
||||||
:h2="group.label"
|
:h2="group.label"
|
||||||
|
@ -135,33 +137,30 @@ const save = async () => {
|
||||||
v-bind="setting.fieldParams"
|
v-bind="setting.fieldParams"
|
||||||
v-model="values[setting.identifier]"
|
v-model="values[setting.identifier]"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable vue/valid-v-model -->
|
|
||||||
<signup-form-builder
|
<signup-form-builder
|
||||||
v-else-if="setting.fieldType === 'formBuilder'"
|
v-else-if="setting.fieldType === 'formBuilder'"
|
||||||
v-model="values[setting.identifier] as Form"
|
v-model="values[setting.identifier] as Form"
|
||||||
:signup-approval-enabled="!!values.moderation__signup_approval_enabled"
|
:signup-approval-enabled="!!values.moderation__signup_approval_enabled"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-enable vue/valid-v-model -->
|
|
||||||
<Input
|
<Input
|
||||||
v-else-if="setting.field.widget.class === 'PasswordInput'"
|
v-else-if="setting.field.widget.class === 'PasswordInput'"
|
||||||
v-model="values[setting.identifier]"
|
v-model="values[setting.identifier] as string"
|
||||||
password
|
password
|
||||||
type="password"
|
type="password"
|
||||||
class="ui input"
|
class="ui input"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
v-else-if="setting.field.widget.class === 'TextInput'"
|
v-else-if="setting.field.widget.class === 'TextInput'"
|
||||||
v-model="values[setting.identifier]"
|
v-model="values[setting.identifier] as string"
|
||||||
type="text"
|
type="text"
|
||||||
class="ui input"
|
class="ui input"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
v-else-if="setting.field.class === 'IntegerField'"
|
v-else-if="setting.field.class === 'IntegerField'"
|
||||||
v-model.number="values[setting.identifier]"
|
v-model.number="values[setting.identifier] as number"
|
||||||
type="number"
|
type="number"
|
||||||
class="ui input"
|
class="ui input"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable vue/valid-v-model -->
|
|
||||||
<textarea
|
<textarea
|
||||||
v-else-if="setting.field.widget.class === 'Textarea'"
|
v-else-if="setting.field.widget.class === 'Textarea'"
|
||||||
v-model="values[setting.identifier] as string"
|
v-model="values[setting.identifier] as string"
|
||||||
|
@ -172,13 +171,11 @@ const save = async () => {
|
||||||
<div
|
<div
|
||||||
v-else-if="setting.field.widget.class === 'CheckboxInput'"
|
v-else-if="setting.field.widget.class === 'CheckboxInput'"
|
||||||
>
|
>
|
||||||
<!-- eslint-disable vue/valid-v-model -->
|
|
||||||
<Toggle
|
<Toggle
|
||||||
v-model="values[setting.identifier] as boolean"
|
v-model="values[setting.identifier] as boolean"
|
||||||
big
|
big
|
||||||
:label="setting.verbose_name"
|
:label="setting.verbose_name"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-enable vue/valid-v-model -->
|
|
||||||
<Spacer :size="8" />
|
<Spacer :size="8" />
|
||||||
<p v-if="setting.help_text">
|
<p v-if="setting.help_text">
|
||||||
{{ setting.help_text }}
|
{{ setting.help_text }}
|
||||||
|
@ -215,11 +212,15 @@ const save = async () => {
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<div v-else-if="setting.field.widget.class === 'ImageWidget'">
|
<div v-else-if="setting.field.widget.class === 'ImageWidget'">
|
||||||
|
<!-- TODO: Implement image input -->
|
||||||
|
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<Input
|
<Input
|
||||||
:id="setting.identifier"
|
:id="setting.identifier"
|
||||||
:ref="setFileRef(setting.identifier)"
|
:ref="setFileRef(setting.identifier)"
|
||||||
type="file"
|
type="file"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="values[setting.identifier]">
|
<div v-if="values[setting.identifier]">
|
||||||
<h3 class="ui header">
|
<h3 class="ui header">
|
||||||
{{ t('components.admin.SettingsGroup.header.image') }}
|
{{ t('components.admin.SettingsGroup.header.image') }}
|
||||||
|
@ -271,6 +272,7 @@ const save = async () => {
|
||||||
</Section>
|
</Section>
|
||||||
<hr :class="$style.separator">
|
<hr :class="$style.separator">
|
||||||
<Spacer size-64 />
|
<Spacer size-64 />
|
||||||
|
<!-- eslint-enable vue/valid-v-model -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
|
|
|
@ -13,8 +13,6 @@ import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
|
||||||
import { type Album } from '~/types'
|
import { type Album } from '~/types'
|
||||||
|
|
||||||
const play = defineEmit<[album: Album]>()
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
album: Album;
|
album: Album;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import type { components, operations } from '~/generated/types.ts'
|
import type { components } from '~/generated/types.ts'
|
||||||
|
|
||||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||||
import Card from '~/components/ui/Card.vue'
|
import Card from '~/components/ui/Card.vue'
|
||||||
import Spacer from '~/components/ui/Spacer.vue'
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
|
||||||
import type { Artist, Cover, Track, Album } from '~/types'
|
import type { Artist, Album } from '~/types'
|
||||||
|
|
||||||
const albums = ref([] as Album[])
|
const albums = ref([] as Album[])
|
||||||
const tracks = ref([] as Track[])
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
artist: Artist | components['schemas']['ArtistWithAlbums'];
|
artist: Artist | components['schemas']['ArtistWithAlbums'];
|
||||||
|
|
|
@ -79,7 +79,7 @@ watch(
|
||||||
<template>
|
<template>
|
||||||
<Section
|
<Section
|
||||||
align-left
|
align-left
|
||||||
columns-per-item="1"
|
:columns-per-item="1"
|
||||||
:h2="title"
|
:h2="title"
|
||||||
>
|
>
|
||||||
<Loader
|
<Loader
|
||||||
|
|
|
@ -14,6 +14,9 @@ interface Props {
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// TODO: Fix getRoute
|
// TODO: Fix getRoute
|
||||||
|
|
||||||
|
// TODO: check if still needed:
|
||||||
|
/*
|
||||||
const getRoute = (ac: ArtistCredit) => {
|
const getRoute = (ac: ArtistCredit) => {
|
||||||
return {
|
return {
|
||||||
name: ac.artist.channel ? 'channels.detail' : 'library.artists.detail',
|
name: ac.artist.channel ? 'channels.detail' : 'library.artists.detail',
|
||||||
|
@ -22,6 +25,7 @@ const getRoute = (ac: ArtistCredit) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface Props {
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const route = computed(() => props.artist.channel
|
const route = computed(() => props.artist.channel
|
||||||
? { name: 'channels.detail', params: { id: props.artist.channel.uuid } }
|
? { name: 'channels.detail', params: { id: props.artist.channel } }
|
||||||
: { name: 'library.artists.detail', params: { id: props.artist.id } }
|
: { name: 'library.artists.detail', params: { id: props.artist.id } }
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { momentFormat } from '~/utils/filters'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
|
@ -20,7 +19,6 @@ interface Props {
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const imageUrl = computed(() => props.object.artist?.cover
|
const imageUrl = computed(() => props.object.artist?.cover
|
||||||
? store.getters['instance/absoluteUrl'](props.object.artist.cover.urls.medium_square_crop)
|
? store.getters['instance/absoluteUrl'](props.object.artist.cover.urls.medium_square_crop)
|
||||||
|
|
|
@ -79,6 +79,8 @@ interface MetadataChoices {
|
||||||
const metadataChoices = ref({ itunes_category: null } as MetadataChoices)
|
const metadataChoices = ref({ itunes_category: null } as MetadataChoices)
|
||||||
const itunesSubcategories = computed(() => {
|
const itunesSubcategories = computed(() => {
|
||||||
for (const element of metadataChoices.value.itunes_category ?? []) {
|
for (const element of metadataChoices.value.itunes_category ?? []) {
|
||||||
|
// TODO: Backend: Define schema for `metadata` field
|
||||||
|
// @ts-expect-error No types defined by backend schema for `metadata` field
|
||||||
if (element.value === newValues.metadata.itunes_category) {
|
if (element.value === newValues.metadata.itunes_category) {
|
||||||
return element.children ?? []
|
return element.children ?? []
|
||||||
}
|
}
|
||||||
|
@ -94,6 +96,7 @@ const labels = computed(() => ({
|
||||||
|
|
||||||
const submittable = computed(() => !!(
|
const submittable = computed(() => !!(
|
||||||
newValues.content_category === 'podcast'
|
newValues.content_category === 'podcast'
|
||||||
|
// @ts-expect-error No types defined by backend schema for `metadata` field
|
||||||
? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language
|
? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language
|
||||||
: newValues.name && newValues.username
|
: newValues.name && newValues.username
|
||||||
))
|
))
|
||||||
|
@ -104,13 +107,16 @@ watch(() => newValues.name, (name) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// @ts-expect-error No types defined by backend schema for `metadata` field
|
||||||
watch(() => newValues.metadata.itunes_category, () => {
|
watch(() => newValues.metadata.itunes_category, () => {
|
||||||
|
// @ts-expect-error No types defined by backend schema for `metadata` field
|
||||||
newValues.metadata.itunes_subcategory = null
|
newValues.metadata.itunes_subcategory = null
|
||||||
})
|
})
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const errors = ref([] as string[])
|
const errors = ref([] as string[])
|
||||||
|
|
||||||
|
// @ts-expect-error Re-check emits
|
||||||
watchEffect(() => emit('category', newValues.content_category))
|
watchEffect(() => emit('category', newValues.content_category))
|
||||||
watchEffect(() => emit('loading', isLoading.value))
|
watchEffect(() => emit('loading', isLoading.value))
|
||||||
watchEffect(() => emit('submittable', submittable.value))
|
watchEffect(() => emit('submittable', submittable.value))
|
||||||
|
@ -265,6 +271,8 @@ defineExpose({
|
||||||
<label for="channel-language">
|
<label for="channel-language">
|
||||||
{{ t('components.audio.ChannelForm.label.language') }}
|
{{ t('components.audio.ChannelForm.label.language') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<select
|
<select
|
||||||
id="channel-language"
|
id="channel-language"
|
||||||
v-model="newValues.metadata.language"
|
v-model="newValues.metadata.language"
|
||||||
|
@ -295,6 +303,8 @@ defineExpose({
|
||||||
<label for="channel-itunes-category">
|
<label for="channel-itunes-category">
|
||||||
{{ t('components.audio.ChannelForm.label.category') }}
|
{{ t('components.audio.ChannelForm.label.category') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<select
|
<select
|
||||||
id="itunes-category"
|
id="itunes-category"
|
||||||
v-model="newValues.metadata.itunes_category"
|
v-model="newValues.metadata.itunes_category"
|
||||||
|
@ -315,6 +325,8 @@ defineExpose({
|
||||||
<label for="channel-itunes-category">
|
<label for="channel-itunes-category">
|
||||||
{{ t('components.audio.ChannelForm.label.subcategory') }}
|
{{ t('components.audio.ChannelForm.label.subcategory') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<select
|
<select
|
||||||
id="itunes-category"
|
id="itunes-category"
|
||||||
v-model="newValues.metadata.itunes_subcategory"
|
v-model="newValues.metadata.itunes_subcategory"
|
||||||
|
@ -342,6 +354,7 @@ defineExpose({
|
||||||
</span>
|
</span>
|
||||||
</Alert>
|
</Alert>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<Input
|
<Input
|
||||||
id="channel-itunes-email"
|
id="channel-itunes-email"
|
||||||
v-model="newValues.metadata.owner_email"
|
v-model="newValues.metadata.owner_email"
|
||||||
|
@ -351,6 +364,7 @@ defineExpose({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
|
<!-- @vue-ignore -->
|
||||||
<Input
|
<Input
|
||||||
id="channel-itunes-name"
|
id="channel-itunes-name"
|
||||||
v-model="newValues.metadata.owner_name"
|
v-model="newValues.metadata.owner_name"
|
||||||
|
|
|
@ -18,7 +18,10 @@ interface Props {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const width = ref(null)
|
|
||||||
|
// TODO: This used to be `null`. Is `0` correct?
|
||||||
|
const width = ref(0)
|
||||||
|
|
||||||
const height = ref(150)
|
const height = ref(150)
|
||||||
const minHeight = ref(100)
|
const minHeight = ref(100)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
|
import type { components } from '~/generated/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
@ -29,12 +30,12 @@ interface Props extends PlayOptionsProps {
|
||||||
isPlayable?: boolean
|
isPlayable?: boolean
|
||||||
tracks?: Track[]
|
tracks?: Track[]
|
||||||
track?: Track | null
|
track?: Track | null
|
||||||
artist?: Artist | null
|
artist?: Artist | components["schemas"]["SimpleChannelArtist"] | components['schemas']['ArtistWithAlbums'] | null
|
||||||
album?: Album | null
|
album?: Album | null
|
||||||
playlist?: Playlist | null
|
playlist?: Playlist | null
|
||||||
library?: Library | null
|
library?: Library | null
|
||||||
channel?: Channel | null
|
channel?: Channel | null
|
||||||
account?: Actor | null
|
account?: Actor | components['schemas']['APIActor'] | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
@ -201,7 +202,7 @@ const isOpen = ref(false)
|
||||||
</span>
|
</span>
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
|
||||||
<hr v-if="filterableArtist || Object.keys(getReportableObjects({track, album, artist, playlist, account, channel})).length > 0">
|
<hr v-if="filterableArtist || Object.keys(getReportableObjects({ track, album, artist, playlist, account, channel })).length > 0">
|
||||||
|
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-if="filterableArtist"
|
v-if="filterableArtist"
|
||||||
|
@ -214,7 +215,7 @@ const isOpen = ref(false)
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-for="obj in getReportableObjects({track, album, artist, playlist, account, channel})"
|
v-for="obj in getReportableObjects({ track, album, artist, playlist, account, channel })"
|
||||||
:key="obj.target.type + obj.target.id"
|
:key="obj.target.type + obj.target.id"
|
||||||
icon="bi-exclamation-triangle-fill"
|
icon="bi-exclamation-triangle-fill"
|
||||||
@click.stop.prevent="report(obj)"
|
@click.stop.prevent="report(obj)"
|
||||||
|
|
|
@ -119,17 +119,18 @@ const loopingTitle = computed(() => {
|
||||||
: t('components.audio.Player.label.loopingWholeQueue')
|
: t('components.audio.Player.label.loopingWholeQueue')
|
||||||
})
|
})
|
||||||
|
|
||||||
const hideArtist = () => {
|
// TODO: check if still useful for filtering
|
||||||
if (currentTrack.value.artistId !== -1 && currentTrack.value.artistCredit) {
|
// const hideArtist = () => {
|
||||||
return store.dispatch('moderation/hide', {
|
// if (currentTrack.value.artistId !== -1 && currentTrack.value.artistCredit) {
|
||||||
type: 'artist',
|
// return store.dispatch('moderation/hide', {
|
||||||
target: {
|
// type: 'artist',
|
||||||
id: currentTrack.value.artistCredit[0].artist.id,
|
// target: {
|
||||||
name: currentTrack.value.artistCredit[0].artist.name
|
// id: currentTrack.value.artistCredit[0].artist.id,
|
||||||
}
|
// name: currentTrack.value.artistCredit[0].artist.name
|
||||||
})
|
// }
|
||||||
}
|
// })
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { useQueue } from '~/composables/audio/queue'
|
||||||
|
|
||||||
import Button from '~/components/ui/Button.vue'
|
import Button from '~/components/ui/Button.vue'
|
||||||
|
|
||||||
const { playPrevious, hasNext, playNext, currentTrack } = useQueue()
|
// TODO: Check if we want to use `currentTrack` from useQueue() in order to disable some icon. Or not.
|
||||||
|
const { playPrevious, hasNext, playNext } = useQueue()
|
||||||
const { isPlaying } = usePlayer()
|
const { isPlaying } = usePlayer()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
@ -1,49 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Artist, Track, Album, Tag } from '~/types'
|
|
||||||
import type { RouteRecordName, RouteLocationNamedRaw } from 'vue-router'
|
|
||||||
|
|
||||||
import jQuery from 'jquery'
|
import { useFocus } from '@vueuse/core'
|
||||||
import { trim } from 'lodash-es'
|
|
||||||
import { useFocus, useCurrentElement } from '@vueuse/core'
|
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useStore } from '~/store'
|
|
||||||
import { generateTrackCreditString } from '~/utils/utils'
|
|
||||||
|
|
||||||
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
|
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
|
||||||
|
|
||||||
interface Events {
|
|
||||||
(e: 'search'): void
|
|
||||||
}
|
|
||||||
|
|
||||||
type CategoryCode = 'federation' | 'podcasts' | 'artists' | 'albums' | 'tracks' | 'tags' | 'more'
|
|
||||||
interface Category {
|
|
||||||
code: CategoryCode,
|
|
||||||
name: string,
|
|
||||||
route: RouteRecordName
|
|
||||||
getId: (obj: unknown) => number
|
|
||||||
getTitle: (obj: unknown) => string
|
|
||||||
getDescription: (obj: unknown) => string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SimpleCategory = Partial<Category> & Pick<Category, 'code' | 'name'>
|
|
||||||
const isCategoryGuard = (object: Category | SimpleCategory): object is Category => typeof object.route === 'string'
|
|
||||||
|
|
||||||
interface Results {
|
|
||||||
name: string,
|
|
||||||
results: Result[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Result {
|
|
||||||
title: string
|
|
||||||
id?: number
|
|
||||||
description?: string
|
|
||||||
routerUrl: RouteLocationNamedRaw
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
|
||||||
|
|
||||||
const search = ref()
|
const search = ref()
|
||||||
const { focused } = useFocus(search)
|
const { focused } = useFocus(search)
|
||||||
onKeyboardShortcut(['shift', 'f'], () => (focused.value = true), true)
|
onKeyboardShortcut(['shift', 'f'], () => (focused.value = true), true)
|
||||||
|
@ -60,8 +23,6 @@ const labels = computed(() => ({
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const store = useStore()
|
|
||||||
const el = useCurrentElement()
|
|
||||||
const query = ref()
|
const query = ref()
|
||||||
|
|
||||||
const enter = () => {
|
const enter = () => {
|
||||||
|
@ -76,66 +37,6 @@ const blur = () => {
|
||||||
search.value.blur()
|
search.value.blur()
|
||||||
}
|
}
|
||||||
|
|
||||||
const categories = computed(() => [
|
|
||||||
{
|
|
||||||
code: 'federation',
|
|
||||||
name: t('components.audio.SearchBar.label.category.federation')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'podcasts',
|
|
||||||
name: t('components.audio.SearchBar.label.category.podcasts')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'artists',
|
|
||||||
route: 'library.artists.detail',
|
|
||||||
name: labels.value.artist,
|
|
||||||
getId: (obj: Artist) => obj.id,
|
|
||||||
getTitle: (obj: Artist) => obj.name,
|
|
||||||
getDescription: () => ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'albums',
|
|
||||||
route: 'library.albums.detail',
|
|
||||||
name: labels.value.album,
|
|
||||||
getId: (obj: Album) => obj.id,
|
|
||||||
getTitle: (obj: Album) => obj.title,
|
|
||||||
getDescription: (obj: Album) => generateTrackCreditString(obj)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'tracks',
|
|
||||||
route: 'library.tracks.detail',
|
|
||||||
name: labels.value.track,
|
|
||||||
getId: (obj: Track) => obj.id,
|
|
||||||
getTitle: (obj: Track) => obj.title,
|
|
||||||
getDescription: (track: Track) => {
|
|
||||||
const album = track.album ?? null
|
|
||||||
return generateTrackCreditString(album) ?? generateTrackCreditString(track) ?? ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'tags',
|
|
||||||
route: 'library.tags.detail',
|
|
||||||
name: labels.value.tag,
|
|
||||||
getId: (obj: Tag) => obj.name,
|
|
||||||
getTitle: (obj: Tag) => `#${obj.name}`,
|
|
||||||
getDescription: (obj: Tag) => ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'more',
|
|
||||||
name: ''
|
|
||||||
}
|
|
||||||
] as (Category | SimpleCategory)[])
|
|
||||||
|
|
||||||
const objectId = computed(() => {
|
|
||||||
const trimmedQuery = trim(trim(query.value), '@')
|
|
||||||
|
|
||||||
if (trimmedQuery.startsWith('http://') || trimmedQuery.startsWith('https://') || trimmedQuery.includes('@')) {
|
|
||||||
return query.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// TODO: Find out what jQuery version supports `search`
|
// TODO: Find out what jQuery version supports `search`
|
||||||
// jQuery(el.value).search({
|
// jQuery(el.value).search({
|
||||||
|
|
|
@ -18,7 +18,7 @@ const { t } = useI18n()
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const cover = computed(() => !props.artist.cover?.urls.original
|
const cover = computed(() => !props.artist.cover?.urls.original
|
||||||
? props.artist.albums.find(album => !!album.cover?.urls.original)?.cover
|
? undefined // TODO: Also check Albums. Like in props.artist.albums.find(album => !!album.cover?.urls.original)?.cover
|
||||||
: props.artist.cover
|
: props.artist.cover
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ const imageUrl = computed(() => cover.value?.urls.original
|
||||||
>
|
>
|
||||||
<play-button
|
<play-button
|
||||||
:icon-only="true"
|
:icon-only="true"
|
||||||
:is-playable="artist.is_playable"
|
:is-playable="true /* TODO: check if artist.is_playable exists instead */"
|
||||||
:button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']"
|
:button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']"
|
||||||
:artist="artist"
|
:artist="artist"
|
||||||
/>
|
/>
|
||||||
|
@ -67,15 +67,15 @@ const imageUrl = computed(() => cover.value?.urls.original
|
||||||
</div>
|
</div>
|
||||||
<div class="extra content">
|
<div class="extra content">
|
||||||
<span v-if="artist.content_category === 'music'">
|
<span v-if="artist.content_category === 'music'">
|
||||||
{{ t('components.audio.artist.Card.meta.tracks', artist.tracks_count) }}
|
{{ t('components.audio.artist.Card.meta.tracks', (0 /* TODO: check where artist.tracks_count exists */)) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ t('components.audio.artist.Card.meta.episodes', artist.tracks_count) }}
|
{{ t('components.audio.artist.Card.meta.episodes', (0 /* TODO: check where artist.tracks_count exists */)) }}
|
||||||
</span>
|
</span>
|
||||||
<play-button
|
<play-button
|
||||||
class="right floated basic icon"
|
class="right floated basic icon"
|
||||||
:dropdown-only="true"
|
:dropdown-only="true"
|
||||||
:is-playable="artist.is_playable"
|
:is-playable="true /* TODO: check if is_playable can be derived from the data */"
|
||||||
:dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']"
|
:dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']"
|
||||||
:artist="artist"
|
:artist="artist"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
|
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
|
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, Album, Playlist, Library, Channel, Actor, Cover, ArtistCredit } from '~/types'
|
import type { Track, Album, Playlist, Library, Channel, Actor, Cover, ArtistCredit } from '~/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
|
import { getArtistCoverUrl } from '~/utils/utils'
|
||||||
|
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
|
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
@ -13,7 +13,7 @@ import usePlayOptions from '~/composables/audio/usePlayOptions'
|
||||||
|
|
||||||
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
||||||
import TrackModal from '~/components/audio/track/Modal.vue'
|
import TrackModal from '~/components/audio/track/Modal.vue'
|
||||||
import { generateTrackCreditString, getArtistCoverUrl } from '~/utils/utils'
|
import { generateTrackCreditString } from '~/utils/utils'
|
||||||
|
|
||||||
interface Props extends PlayOptionsProps {
|
interface Props extends PlayOptionsProps {
|
||||||
track: Track
|
track: Track
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||||
import usePlayOptions from '~/composables/audio/usePlayOptions'
|
import usePlayOptions from '~/composables/audio/usePlayOptions'
|
||||||
import useReport from '~/composables/moderation/useReport'
|
import useReport from '~/composables/moderation/useReport'
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
import type { Track } from '~/types'
|
import type { Track } from '~/types'
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { clone, uniqBy, sortedUniq } from 'lodash-es'
|
import { clone, uniqBy } from 'lodash-es'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useStore } from '~/store'
|
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
|
@ -91,7 +90,6 @@ const allTracks = computed(() => {
|
||||||
const paginateResults = computed(() => props.paginateResults && allTracks.value.length < props.paginateBy)
|
const paginateResults = computed(() => props.paginateResults && allTracks.value.length < props.paginateBy)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
const labels = computed(() => ({
|
const labels = computed(() => ({
|
||||||
title: t('components.audio.track.Table.table.header.title'),
|
title: t('components.audio.track.Table.table.header.title'),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import type { BackendError } from '~/types'
|
import type { BackendError } from '~/types'
|
||||||
import { onBeforeRouteLeave, type RouteLocationRaw, useRouter } from 'vue-router'
|
import { onBeforeRouteLeave, type RouteLocationRaw, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { useEventListener } from '@vueuse/core'
|
import { useEventListener } from '@vueuse/core'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
|
@ -13,7 +13,6 @@ import Alert from '~/components/ui/Alert.vue'
|
||||||
import Input from '~/components/ui/Input.vue'
|
import Input from '~/components/ui/Input.vue'
|
||||||
import Textarea from '~/components/ui/Textarea.vue'
|
import Textarea from '~/components/ui/Textarea.vue'
|
||||||
import Button from '~/components/ui/Button.vue'
|
import Button from '~/components/ui/Button.vue'
|
||||||
import Spacer from '~/components/ui/Spacer.vue'
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
|
||||||
import useLogger from '~/composables/useLogger'
|
import useLogger from '~/composables/useLogger'
|
||||||
|
|
|
@ -33,7 +33,7 @@ const fetchAlbums = async () => {
|
||||||
|
|
||||||
watch(() => model.value.channel, fetchAlbums, { immediate: true })
|
watch(() => model.value.channel, fetchAlbums, { immediate: true })
|
||||||
watch(albums, (value) => {
|
watch(albums, (value) => {
|
||||||
if (value.length === 1) { selectedAlbumId.value = albums.value[0].id }
|
if (value.length === 1) { model.value.albumId = albums.value[0].id }
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
import type { BackendError, Channel, Upload, Track, Album } from '~/types'
|
import type { BackendError, Channel, Upload, Track, Album } from '~/types'
|
||||||
import type { VueUploadItem } from 'vue-upload-component'
|
import type { VueUploadItem } from 'vue-upload-component'
|
||||||
|
|
||||||
import { computed, ref, reactive, watchEffect, watch, onMounted } from 'vue'
|
import { computed, ref, reactive, watchEffect, watch } from 'vue'
|
||||||
import { whenever } from '@vueuse/core'
|
import { whenever } from '@vueuse/core'
|
||||||
import { humanSize } from '~/utils/filters'
|
import { humanSize } from '~/utils/filters'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { useModal } from '~/ui/composables/useModal.ts'
|
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { type paths, type operations, type components } from '~/generated/types.ts'
|
import { type paths, type operations, type components } from '~/generated/types.ts'
|
||||||
|
@ -16,14 +15,12 @@ import UploadMetadataForm from '~/components/channels/UploadMetadataForm.vue'
|
||||||
import FileUploadWidget from '~/components/library/FileUploadWidget.vue'
|
import FileUploadWidget from '~/components/library/FileUploadWidget.vue'
|
||||||
import LicenseSelect from '~/components/channels/LicenseSelect.vue'
|
import LicenseSelect from '~/components/channels/LicenseSelect.vue'
|
||||||
import AlbumSelect from '~/components/channels/AlbumSelect.vue'
|
import AlbumSelect from '~/components/channels/AlbumSelect.vue'
|
||||||
import AlbumModal from '~/components/channels/AlbumModal.vue'
|
|
||||||
|
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
import Alert from '~/components/ui/Alert.vue'
|
import Alert from '~/components/ui/Alert.vue'
|
||||||
import Button from '~/components/ui/Button.vue'
|
import Button from '~/components/ui/Button.vue'
|
||||||
import Link from '~/components/ui/Link.vue'
|
|
||||||
import Loader from '~/components/ui/Loader.vue'
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
import Spacer from '~/components/ui/Spacer.vue'
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
})
|
})
|
||||||
|
|
||||||
const newValues = reactive<Values>({
|
const newValues = reactive<Values>({
|
||||||
|
position: 0,
|
||||||
description: '',
|
description: '',
|
||||||
title: '',
|
title: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Actor } from '~/types'
|
import type { Actor } from '~/types'
|
||||||
|
import type { components } from '~/generated/types'
|
||||||
|
|
||||||
import { toRefs } from '@vueuse/core'
|
import { toRefs } from '@vueuse/core'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { truncate } from '~/utils/filters'
|
import { truncate } from '~/utils/filters'
|
||||||
|
|
||||||
import Link from '~/components/ui/Link.vue'
|
|
||||||
import Pill from '~/components/ui/Pill.vue'
|
import Pill from '~/components/ui/Pill.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
actor: Actor
|
actor: Actor | components['schemas']['APIActor']
|
||||||
avatar?: boolean
|
avatar?: boolean
|
||||||
admin?: boolean
|
admin?: boolean
|
||||||
displayName?: boolean
|
displayName?: boolean
|
||||||
|
@ -32,7 +32,7 @@ const repr = computed(() => {
|
||||||
? actor.value.preferred_username
|
? actor.value.preferred_username
|
||||||
: actor.value.full_username
|
: actor.value.full_username
|
||||||
|
|
||||||
return truncate(name, truncateLength.value)
|
return truncate(name || '', truncateLength.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const url = computed(() => {
|
const url = computed(() => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
|
||||||
|
import type { components } from '~/generated/types'
|
||||||
import type { ContentFilter } from '~/store/moderation'
|
import type { ContentFilter } from '~/store/moderation'
|
||||||
|
|
||||||
import { useCurrentElement } from '@vueuse/core'
|
import { useCurrentElement } from '@vueuse/core'
|
||||||
|
@ -15,12 +16,12 @@ export interface PlayOptionsProps {
|
||||||
isPlayable?: boolean
|
isPlayable?: boolean
|
||||||
tracks?: Track[]
|
tracks?: Track[]
|
||||||
track?: Track | null
|
track?: Track | null
|
||||||
artist?: Artist | null
|
artist?: Artist | components["schemas"]["SimpleChannelArtist"] | components['schemas']['ArtistWithAlbums'] | null
|
||||||
album?: Album | null
|
album?: Album | null
|
||||||
playlist?: Playlist | null
|
playlist?: Playlist | null
|
||||||
library?: Library | null
|
library?: Library | null
|
||||||
channel?: Channel | null
|
channel?: Channel | null
|
||||||
account?: Actor | null
|
account?: Actor | components['schemas']['APIActor'] | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (props: PlayOptionsProps) => {
|
export default (props: PlayOptionsProps) => {
|
||||||
|
@ -36,8 +37,12 @@ export default (props: PlayOptionsProps) => {
|
||||||
if (props.track) {
|
if (props.track) {
|
||||||
return props.track.uploads?.length > 0
|
return props.track.uploads?.length > 0
|
||||||
} else if (props.artist) {
|
} else if (props.artist) {
|
||||||
|
// TODO: Find out how to get tracks, album from Artist
|
||||||
|
|
||||||
|
/*
|
||||||
return props.artist.tracks_count > 0
|
return props.artist.tracks_count > 0
|
||||||
|| props.artist?.albums?.some((album) => album.is_playable === true)
|
|| props.artist?.albums?.some((album) => album.is_playable === true)
|
||||||
|
*/
|
||||||
} else if (props.tracks) {
|
} else if (props.tracks) {
|
||||||
return props.tracks?.some((track) => (track.uploads?.length ?? 0) > 0)
|
return props.tracks?.some((track) => (track.uploads?.length ?? 0) > 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Track, Artist, Album, Playlist, Library, Channel, Actor, ArtistCredit } from '~/types'
|
import type { Track, Artist, Album, Playlist, Library, Channel, Actor, ArtistCredit } from '~/types'
|
||||||
|
import type { components } from '~/generated/types'
|
||||||
|
|
||||||
import { i18n } from '~/init/locale'
|
import { i18n } from '~/init/locale'
|
||||||
|
|
||||||
|
@ -8,11 +9,11 @@ const { t } = i18n.global
|
||||||
|
|
||||||
interface Objects {
|
interface Objects {
|
||||||
track?: Track | null
|
track?: Track | null
|
||||||
album?: Album | null
|
album?: Album | components['schemas']['TrackAlbum'] | null
|
||||||
artist?: Artist | null
|
artist?: Artist | components['schemas']['ArtistWithAlbums'] | components["schemas"]["SimpleChannelArtist"] | null
|
||||||
artistCredit?: ArtistCredit[] | null
|
artistCredit?: ArtistCredit[] | null
|
||||||
playlist?: Playlist | null
|
playlist?: Playlist | null
|
||||||
account?: Actor | null
|
account?: Actor | components['schemas']['APIActor'] | null
|
||||||
library?: Library | null
|
library?: Library | null
|
||||||
channel?: Channel | null
|
channel?: Channel | null
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue