feat(style): consistent look and use of artist credit label on detail pages
This commit is contained in:
parent
75d4ac5467
commit
966181e480
|
@ -2,7 +2,7 @@
|
|||
import type { ArtistCredit } from '~/types'
|
||||
import { useStore } from '~/store'
|
||||
|
||||
import Link from '~/components/ui/Link.vue'
|
||||
import Pill from '~/components/ui/Pill.vue'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
|
@ -23,31 +23,28 @@ const getRoute = (ac: ArtistCredit) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="artist-label ui image label">
|
||||
<template
|
||||
v-for="ac in props.artistCredit"
|
||||
:key="ac.artist.id"
|
||||
<template
|
||||
v-for="ac in props.artistCredit"
|
||||
:key="ac.artist.id"
|
||||
>
|
||||
<router-link
|
||||
:to="{name: 'library.artists.detail', params: {id: ac.artist.id }}"
|
||||
@click.stop.prevent=""
|
||||
>
|
||||
<Link
|
||||
solid
|
||||
secondary
|
||||
round
|
||||
min-content
|
||||
:to="getRoute(ac)"
|
||||
>
|
||||
<img
|
||||
v-if="ac.index === 0 && ac.artist.cover && ac.artist.cover.urls.original"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](ac.artist.cover.urls.medium_square_crop)"
|
||||
alt=""
|
||||
:class="[{circular: ac.artist.content_category != 'podcast'}]"
|
||||
>
|
||||
<i
|
||||
v-else-if="ac.index === 0"
|
||||
:class="[ac.artist.content_category != 'podcast' ? 'circular' : 'bordered', 'inverted violet bi bi-users icon']"
|
||||
/>
|
||||
<Pill>
|
||||
<template #image>
|
||||
<img
|
||||
v-if="ac.artist.cover && ac.artist.cover.urls.original"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](ac.artist.cover.urls.small_square_crop)"
|
||||
:alt="ac.artist.name"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="bi bi-person-circle"
|
||||
/>
|
||||
</template>
|
||||
{{ ac.credit }}
|
||||
</Link>
|
||||
<span>{{ ac.joinphrase }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</Pill>
|
||||
</router-link>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -69,7 +69,7 @@ const labels = computed(() => ({
|
|||
|
||||
const {
|
||||
isShuffled,
|
||||
shuffle
|
||||
shuffle,
|
||||
} = useQueue()
|
||||
|
||||
const isLoading = ref(false)
|
||||
|
@ -150,10 +150,7 @@ const remove = async () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Layout
|
||||
stack
|
||||
main
|
||||
>
|
||||
<Layout stack main>
|
||||
<Loader
|
||||
v-if="isLoading"
|
||||
v-title="labels.title"
|
||||
|
@ -165,129 +162,105 @@ const remove = async () => {
|
|||
v-lazy="store.getters['instance/absoluteUrl'](object.cover.urls.large_square_crop)"
|
||||
:alt="object.title"
|
||||
class="channel-image"
|
||||
>
|
||||
<img
|
||||
v-else-if="object.artist_credit && object.artist_credit[0] && object.artist_credit[0].artist.cover"
|
||||
v-lazy="object.artist_credit[0].artist.cover.urls.large_square_crop"
|
||||
:alt="object.artist_credit[0].artist.name"
|
||||
class="channel-image"
|
||||
>
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
alt=""
|
||||
class="channel-image"
|
||||
src="../../assets/audio/default-cover.png"
|
||||
>
|
||||
<!-- ({target}) => target -->
|
||||
<!-- Header (TODO: Put into Header component) Hint: Header is heavier fontweight than h1! -->
|
||||
<Layout
|
||||
stack
|
||||
style="flex: 1; gap: 8px;"
|
||||
>
|
||||
<h1>{{ object.title }}</h1>
|
||||
<!-- <Header :h1="object.title" /> -->
|
||||
<artist-credit-label
|
||||
v-if="artistCredit"
|
||||
:artist-credit="artistCredit"
|
||||
/>
|
||||
<!-- Metadata: -->
|
||||
<div class="meta">
|
||||
<template v-if="object.release_date">
|
||||
{{ momentFormat(new Date(object.release_date ?? '1970-01-01'), 'Y') }}
|
||||
<i class="bi bi-dot" />
|
||||
</template>
|
||||
<template v-if="totalTracks > 0">
|
||||
<span v-if="isSerie">
|
||||
{{ t('components.library.AlbumBase.meta.episodes', totalTracks) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('components.library.AlbumBase.meta.tracks', totalTracks) }}
|
||||
</span>
|
||||
</template>
|
||||
<i
|
||||
v-if="totalDuration > 0"
|
||||
class="bi bi-dot"
|
||||
/>
|
||||
<human-duration
|
||||
v-if="totalDuration > 0"
|
||||
:duration="totalDuration"
|
||||
/>
|
||||
<!--TODO: License -->
|
||||
</div>
|
||||
<Layout flex>
|
||||
<rendered-description
|
||||
v-if="object.description"
|
||||
:content="object.description"
|
||||
:can-update="true"
|
||||
/>
|
||||
</Layout>
|
||||
<Layout flex>
|
||||
<PlayButton
|
||||
split
|
||||
:tracks="object.tracks"
|
||||
:is-playable="object.is_playable"
|
||||
/>
|
||||
<Button
|
||||
v-if="object.tracks.length > 2"
|
||||
primary
|
||||
icon="bi-shuffle"
|
||||
:aria-label="labels.shuffle"
|
||||
@click.prevent.stop="shuffle()"
|
||||
>
|
||||
{{ labels.shuffle }}
|
||||
</Button>
|
||||
<DangerousButton
|
||||
v-if="artistCredit[0] &&
|
||||
store.state.auth.authenticated &&
|
||||
artistCredit[0].artist.channel &&
|
||||
artistCredit[0].artist.attributed_to?.full_username === store.state.auth.fullUsername"
|
||||
:is-loading="isLoading"
|
||||
icon="bi-trash"
|
||||
@confirm="remove()"
|
||||
>
|
||||
{{ t('components.library.AlbumDropdown.button.delete') }}
|
||||
</DangerousButton>
|
||||
<Spacer
|
||||
h
|
||||
grow
|
||||
/>
|
||||
<TrackFavoriteIcon
|
||||
v-if="store.state.auth.authenticated"
|
||||
:album="object"
|
||||
/>
|
||||
<TrackPlaylistIcon
|
||||
v-if="store.state.auth.authenticated"
|
||||
:album="object"
|
||||
/>
|
||||
<!-- TODO: Share Button -->
|
||||
<album-dropdown
|
||||
:object="object"
|
||||
:public-libraries="publicLibraries"
|
||||
:is-loading="isLoading"
|
||||
:is-album="isAlbum"
|
||||
:is-serie="isSerie"
|
||||
:is-channel="isChannel"
|
||||
/>
|
||||
<Layout stack style="flex: 1; gap: 8px;">
|
||||
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ object.title }}</h1>
|
||||
<artist-credit-label
|
||||
v-if="artistCredit"
|
||||
:artist-credit="artistCredit"
|
||||
@remove="remove"
|
||||
/>
|
||||
<!-- Metadata: -->
|
||||
<div class="meta">
|
||||
<template v-if="object.release_date">
|
||||
{{ momentFormat(new Date(object.release_date ?? '1970-01-01'), 'Y') }}
|
||||
<i class="bi bi-dot" />
|
||||
</template>
|
||||
<template v-if="totalTracks > 0">
|
||||
<span v-if="isSerie">
|
||||
{{ t('components.library.AlbumBase.meta.episodes', totalTracks) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('components.library.AlbumBase.meta.tracks', totalTracks) }}
|
||||
</span>
|
||||
</template>
|
||||
<i v-if="totalDuration > 0" class="bi bi-dot" />
|
||||
<human-duration
|
||||
v-if="totalDuration > 0"
|
||||
:duration="totalDuration"
|
||||
/>
|
||||
<!--TODO: License -->
|
||||
</div>
|
||||
<Layout flex>
|
||||
<rendered-description
|
||||
v-if="object.description"
|
||||
:content="object.description"
|
||||
:can-update="true"
|
||||
/>
|
||||
</Layout>
|
||||
<Layout flex>
|
||||
<PlayButton
|
||||
split
|
||||
:tracks="object.tracks"
|
||||
:is-playable="object.is_playable"
|
||||
/>
|
||||
<Button
|
||||
v-if="object.tracks.length > 2"
|
||||
primary
|
||||
icon="bi-shuffle"
|
||||
:aria-label="labels.shuffle"
|
||||
@click.prevent.stop="shuffle()"
|
||||
>
|
||||
{{ labels.shuffle }}
|
||||
</Button>
|
||||
<DangerousButton
|
||||
v-if="artistCredit[0] &&
|
||||
store.state.auth.authenticated &&
|
||||
artistCredit[0].artist.channel &&
|
||||
artistCredit[0].artist.attributed_to?.full_username === store.state.auth.fullUsername"
|
||||
:is-loading="isLoading"
|
||||
@confirm="remove()"
|
||||
icon="bi-trash"
|
||||
>
|
||||
{{ t('components.library.AlbumDropdown.button.delete') }}
|
||||
</DangerousButton>
|
||||
<Spacer h grow />
|
||||
<TrackFavoriteIcon v-if="store.state.auth.authenticated" :album="object" />
|
||||
<TrackPlaylistIcon v-if="store.state.auth.authenticated" :album="object" />
|
||||
<!-- TODO: Share Button -->
|
||||
<album-dropdown
|
||||
:object="object"
|
||||
:public-libraries="publicLibraries"
|
||||
:is-loading="isLoading"
|
||||
:is-album="isAlbum"
|
||||
:is-serie="isSerie"
|
||||
:is-channel="isChannel"
|
||||
:artist-credit="artistCredit"
|
||||
@remove="remove"
|
||||
/>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
<div style="flex 1;">
|
||||
<router-view
|
||||
v-if="object"
|
||||
:key="route.fullPath"
|
||||
:paginate-by="paginateBy"
|
||||
:total-tracks="totalTracks"
|
||||
:is-serie="isSerie"
|
||||
:artist-credit="artistCredit"
|
||||
:object="object"
|
||||
:is-loading-tracks="isLoadingTracks"
|
||||
object-type="album"
|
||||
@libraries-loaded="libraries = $event"
|
||||
/>
|
||||
</div>
|
||||
<div style="flex 1;">
|
||||
<router-view
|
||||
v-if="object"
|
||||
:key="route.fullPath"
|
||||
:paginate-by="paginateBy"
|
||||
:total-tracks="totalTracks"
|
||||
:is-serie="isSerie"
|
||||
:artist-credit="artistCredit"
|
||||
:object="object"
|
||||
:is-loading-tracks="isLoadingTracks"
|
||||
object-type="album"
|
||||
@libraries-loaded="libraries = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
|
|
@ -25,6 +25,7 @@ import Layout from '~/components/ui/Layout.vue'
|
|||
import Modal from '~/components/ui/Modal.vue'
|
||||
import Spacer from '~/components/ui/Spacer.vue'
|
||||
|
||||
|
||||
interface Props {
|
||||
id: number
|
||||
}
|
||||
|
@ -55,7 +56,8 @@ const musicbrainzUrl = computed(() => object.value?.mbid ? `https://musicbrainz.
|
|||
const discogsUrl = computed(() => `https://discogs.com/search/?type=artist&title=${encodeURI(object.value?.name ?? '')}`)
|
||||
const publicLibraries = computed(() => libraries.value?.filter(library => library.privacy_level === 'everyone') ?? [])
|
||||
|
||||
const cover = computed(() => {
|
||||
|
||||
const cover = computed(() => {
|
||||
const artistCover: Cover | undefined = object.value?.cover
|
||||
|
||||
const albumCover: Cover | undefined = object.value?.albums
|
||||
|
@ -66,12 +68,12 @@ const cover = computed(() => {
|
|||
)?.cover
|
||||
|
||||
const fallback : Cover = {
|
||||
uuid: '',
|
||||
urls: {
|
||||
original: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`,
|
||||
medium_square_crop: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`,
|
||||
large_square_crop: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`
|
||||
}
|
||||
uuid: '',
|
||||
urls: {
|
||||
original: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`,
|
||||
medium_square_crop: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`,
|
||||
large_square_crop: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`
|
||||
}
|
||||
}
|
||||
|
||||
return artistCover
|
||||
|
@ -120,11 +122,7 @@ watch(() => props.id, fetchData, { immediate: true })
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Layout
|
||||
v-title="labels.title"
|
||||
stack
|
||||
main
|
||||
>
|
||||
<Layout stack main v-title="labels.title">
|
||||
<Loader v-if="isLoading" />
|
||||
<template v-if="object && !isLoading">
|
||||
<Layout flex>
|
||||
|
@ -133,16 +131,9 @@ watch(() => props.id, fetchData, { immediate: true })
|
|||
:alt="object.name"
|
||||
class="channel-image"
|
||||
>
|
||||
<Layout
|
||||
stack
|
||||
style="flex: 1; gap: 8px;"
|
||||
>
|
||||
<h1>{{ object.name }}</h1>
|
||||
<Layout
|
||||
flex
|
||||
class="meta"
|
||||
style="gap: 0;"
|
||||
>
|
||||
<Layout stack style="flex: 1; gap: 8px;">
|
||||
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ object.name }}</h1>
|
||||
<Layout flex class="meta" style="gap: 0;">
|
||||
<div
|
||||
v-if="albums"
|
||||
>
|
||||
|
@ -190,8 +181,8 @@ watch(() => props.id, fetchData, { immediate: true })
|
|||
|
||||
<PopoverItem
|
||||
v-if="publicLibraries.length > 0"
|
||||
icon="bi-code-square"
|
||||
@click="showEmbedModal = true"
|
||||
icon="bi-code-square"
|
||||
>
|
||||
{{ t('components.library.ArtistBase.button.embed') }}
|
||||
</PopoverItem>
|
||||
|
|
|
@ -11,6 +11,7 @@ import { useStore } from '~/store'
|
|||
import axios from 'axios'
|
||||
|
||||
import ActorLink from '~/components/common/ActorLink.vue'
|
||||
import ArtistCreditLabel from '~/components/audio/ArtistCreditLabel.vue'
|
||||
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
||||
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||
|
@ -76,6 +77,8 @@ const attributedToUrl = computed(() => router.resolve({
|
|||
}
|
||||
})?.href)
|
||||
|
||||
const artistCredit = track.value?.artist_credit
|
||||
|
||||
const totalDuration = computed(() => track.value?.uploads[0]?.duration ?? 0)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
@ -126,271 +129,253 @@ watch(showDeleteModal, (newValue) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Layout
|
||||
stack
|
||||
main
|
||||
>
|
||||
<Loader
|
||||
v-if="isLoading"
|
||||
v-title="labels.title"
|
||||
/>
|
||||
<template v-if="track">
|
||||
<Layout flex>
|
||||
<img
|
||||
v-if="track.cover"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](track.cover.urls.large_square_crop)"
|
||||
alt=""
|
||||
class="channel-image"
|
||||
>
|
||||
<img
|
||||
v-if="track.album && track.album.cover"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](track.album.cover.urls.large_square_crop)"
|
||||
alt=""
|
||||
class="channel-image"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
alt=""
|
||||
class="channel-image"
|
||||
src="../../assets/audio/default-cover.png"
|
||||
>
|
||||
<Layout stack main>
|
||||
<Loader
|
||||
v-if="isLoading"
|
||||
v-title="labels.title"
|
||||
/>
|
||||
<template v-if="track">
|
||||
<Layout flex>
|
||||
<img
|
||||
v-if="track.cover"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](track.cover.urls.large_square_crop)"
|
||||
alt=""
|
||||
class="channel-image"
|
||||
>
|
||||
<img
|
||||
v-if="track.album && track.album.cover"
|
||||
v-lazy="store.getters['instance/absoluteUrl'](track.album.cover.urls.large_square_crop)"
|
||||
alt=""
|
||||
class="channel-image"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
alt=""
|
||||
class="channel-image"
|
||||
src="../../assets/audio/default-cover.png"
|
||||
>
|
||||
|
||||
<Layout
|
||||
stack
|
||||
style="flex: 1; gap: 8px;"
|
||||
>
|
||||
<Layout
|
||||
flex
|
||||
no-gap
|
||||
style="align-items: baseline; margin-bottom: 24px;"
|
||||
<Layout stack style="flex: 1; gap: 8px;">
|
||||
<Layout flex no-gap style="align-items: baseline;">
|
||||
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ track.title }}</h1>
|
||||
<Spacer grow />
|
||||
<Button
|
||||
v-if="upload"
|
||||
:aria-label="labels.download"
|
||||
:to="downloadUrl"
|
||||
target="_blank"
|
||||
primary
|
||||
icon="bi-download"
|
||||
:title="labels.download"
|
||||
>
|
||||
<h1>{{ track.title }}</h1>
|
||||
<Spacer grow />
|
||||
<Button
|
||||
v-if="upload"
|
||||
:aria-label="labels.download"
|
||||
:to="downloadUrl"
|
||||
target="_blank"
|
||||
primary
|
||||
icon="bi-download"
|
||||
:title="labels.download"
|
||||
>
|
||||
{{ labels.download }}
|
||||
</Button>
|
||||
</Layout>
|
||||
<div class="meta">
|
||||
{{ labels.download }}
|
||||
</Button>
|
||||
</Layout>
|
||||
<artist-credit-label
|
||||
:artist-credit="track.artist_credit"
|
||||
/>
|
||||
<div class="meta">
|
||||
<span>{{ t('components.library.TrackBase.title') }}</span>
|
||||
<i class="bi bi-dot" />
|
||||
<span>{{ track.album.title }}</span>
|
||||
<i
|
||||
v-if="totalDuration > 0"
|
||||
class="bi bi-dot"
|
||||
/>
|
||||
<i v-if="totalDuration > 0" class="bi bi-dot" />
|
||||
<human-duration
|
||||
v-if="totalDuration > 0"
|
||||
:duration="totalDuration"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Layout flex>
|
||||
<PlayButton
|
||||
:is-playable="track.is_playable"
|
||||
class="vibrant"
|
||||
split
|
||||
:track="track"
|
||||
/>
|
||||
|
||||
<Spacer
|
||||
h
|
||||
grow
|
||||
/>
|
||||
|
||||
<TrackFavoriteIcon
|
||||
v-if="store.state.auth.authenticated"
|
||||
:track="track"
|
||||
/>
|
||||
<TrackPlaylistIcon
|
||||
v-if="store.state.auth.authenticated"
|
||||
:track="track"
|
||||
/>
|
||||
<Popover v-model:open="open">
|
||||
<template #default="{ toggleOpen }">
|
||||
<OptionsButton @click="toggleOpen" />
|
||||
</template>
|
||||
<template #items>
|
||||
<PopoverItem
|
||||
v-if="domain != store.getters['instance/domain']"
|
||||
:to="track.fid"
|
||||
target="_blank"
|
||||
icon="bi-box-arrow-up-right"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.domain', { domain }) }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="isEmbedable"
|
||||
icon="bi-code-slash"
|
||||
@click="showEmbedModal = !showEmbedModal"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.embed') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
:to="wikipediaUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
icon="bi-wikipedia"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.wikipedia') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="discogsUrl"
|
||||
:to="discogsUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
icon="bi-box-arrow-up-right"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.discogs') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="track.is_local"
|
||||
icon="bi-pencil-fill"
|
||||
:to="{ name: 'library.tracks.edit', params: { id: track.id } }"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.edit') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="artist &&
|
||||
store.state.auth.authenticated &&
|
||||
artist.channel &&
|
||||
artist.attributed_to.full_username === store.state.auth.fullUsername"
|
||||
icon="bi-trash"
|
||||
@click="showDeleteModal = true"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.delete') }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-for="obj in getReportableObjects({ track })"
|
||||
:key="obj.target.type + obj.target.id"
|
||||
icon="bi-flag"
|
||||
@click="report(obj)"
|
||||
>
|
||||
{{ obj.label }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-if="store.state.auth.availablePermissions['library']"
|
||||
:to="{
|
||||
name: 'manage.library.tracks.detail',
|
||||
params: { id: track.id }
|
||||
}"
|
||||
icon="bi-wrench"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.moderation') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="store.state.auth.profile?.is_superuser"
|
||||
:to="store.getters['instance/absoluteUrl'](`/api/admin/music/track/${track.id}`)"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon="bi-wrench"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.django') }}
|
||||
</PopoverItem>
|
||||
</template>
|
||||
</Popover>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
<hr>
|
||||
<Layout flex>
|
||||
<div>
|
||||
<span v-if="track.attributed_to">
|
||||
{{ t('components.library.TrackBase.subtitle.with-uploader') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('components.library.TrackBase.subtitle.without-uploader') }}
|
||||
</span>
|
||||
<ActorLink
|
||||
v-if="track.attributed_to"
|
||||
:actor="track.attributed_to"
|
||||
:avatar="false"
|
||||
<Layout flex>
|
||||
<PlayButton
|
||||
:is-playable="track.is_playable"
|
||||
class="vibrant"
|
||||
split
|
||||
:track="track"
|
||||
/>
|
||||
|
||||
<time
|
||||
:title="track.creation_date"
|
||||
:datetime="track.creation_date"
|
||||
>
|
||||
{{ momentFormat(new Date(track.creation_date), 'LL') }}
|
||||
</time>
|
||||
</div>
|
||||
</Layout>
|
||||
<Spacer h grow />
|
||||
|
||||
<Modal
|
||||
v-if="isEmbedable"
|
||||
v-model="showEmbedModal"
|
||||
:title="t('components.library.TrackBase.modal.embed.header')"
|
||||
>
|
||||
<embed-wizard
|
||||
:id="track.id"
|
||||
type="track"
|
||||
<TrackFavoriteIcon v-if="store.state.auth.authenticated" :track="track" />
|
||||
<TrackPlaylistIcon v-if="store.state.auth.authenticated" :track="track" />
|
||||
<Popover v-model:open="open">
|
||||
<template #default="{ toggleOpen }">
|
||||
<OptionsButton @click="toggleOpen" />
|
||||
</template>
|
||||
<template #items>
|
||||
<PopoverItem
|
||||
v-if="domain != store.getters['instance/domain']"
|
||||
:to="track.fid"
|
||||
target="_blank"
|
||||
icon="bi-box-arrow-up-right"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.domain', { domain }) }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="isEmbedable"
|
||||
@click="showEmbedModal = !showEmbedModal"
|
||||
icon="bi-code-slash"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.embed') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
:to="wikipediaUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
icon="bi-wikipedia"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.wikipedia') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="discogsUrl"
|
||||
:to="discogsUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
icon="bi-box-arrow-up-right"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.discogs') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="track.is_local"
|
||||
icon="bi-pencil-fill"
|
||||
:to="{ name: 'library.tracks.edit', params: { id: track.id } }"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.edit') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="artist &&
|
||||
store.state.auth.authenticated &&
|
||||
artist.channel &&
|
||||
artist.attributed_to.full_username === store.state.auth.fullUsername"
|
||||
@click="showDeleteModal = true"
|
||||
icon="bi-trash"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.delete') }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-for="obj in getReportableObjects({ track })"
|
||||
:key="obj.target.type + obj.target.id"
|
||||
@click="report(obj)"
|
||||
icon="bi-flag"
|
||||
>
|
||||
{{ obj.label }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-if="store.state.auth.availablePermissions['library']"
|
||||
:to="{
|
||||
name: 'manage.library.tracks.detail',
|
||||
params: { id: track.id }
|
||||
}"
|
||||
icon="bi-wrench"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.moderation') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="store.state.auth.profile?.is_superuser"
|
||||
:to="store.getters['instance/absoluteUrl'](`/api/admin/music/track/${track.id}`)"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon="bi-wrench"
|
||||
>
|
||||
{{ t('components.library.TrackBase.link.django') }}
|
||||
</PopoverItem>
|
||||
</template>
|
||||
</Popover>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
<hr>
|
||||
<Layout flex>
|
||||
<div>
|
||||
<span v-if="track.attributed_to">
|
||||
{{ t('components.library.TrackBase.subtitle.with-uploader') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('components.library.TrackBase.subtitle.without-uploader') }}
|
||||
</span>
|
||||
<ActorLink
|
||||
v-if="track.attributed_to"
|
||||
:actor="track.attributed_to"
|
||||
:avatar="false"
|
||||
/>
|
||||
|
||||
<template #actions>
|
||||
<Button
|
||||
secondary
|
||||
@click="showEmbedModal = false"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.cancel') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal
|
||||
v-model="showDeleteModal"
|
||||
:title="t('components.library.TrackBase.modal.delete.header')"
|
||||
destructive
|
||||
>
|
||||
<template #alert>
|
||||
<Alert red>
|
||||
{{ t('components.library.TrackBase.modal.delete.content.warning') }}
|
||||
</Alert>
|
||||
</template>
|
||||
<time
|
||||
:title="track.creation_date"
|
||||
:datetime="track.creation_date"
|
||||
>
|
||||
{{ momentFormat(new Date(track.creation_date), 'LL') }}
|
||||
</time>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<template #actions>
|
||||
<Button
|
||||
secondary
|
||||
@click="showDeleteModal = false"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
destructive
|
||||
:is-loading="isLoading"
|
||||
@click="remove()"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.delete') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<router-view
|
||||
v-if="track"
|
||||
:key="route.fullPath"
|
||||
:track="track"
|
||||
:object="track"
|
||||
object-type="track"
|
||||
@libraries-loaded="libraries = $event"
|
||||
<Modal
|
||||
v-if="isEmbedable"
|
||||
v-model="showEmbedModal"
|
||||
:title="t('components.library.TrackBase.modal.embed.header')"
|
||||
>
|
||||
<embed-wizard
|
||||
:id="track.id"
|
||||
type="track"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<Button
|
||||
secondary
|
||||
@click="showEmbedModal = false"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.cancel') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal
|
||||
v-model="showDeleteModal"
|
||||
:title="t('components.library.TrackBase.modal.delete.header')"
|
||||
destructive
|
||||
>
|
||||
<template #alert>
|
||||
<Alert red>
|
||||
{{ t('components.library.TrackBase.modal.delete.content.warning') }}
|
||||
</Alert>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<Button
|
||||
secondary
|
||||
@click="showDeleteModal = false"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
destructive
|
||||
:is-loading="isLoading"
|
||||
@click="remove()"
|
||||
>
|
||||
{{ t('components.library.TrackBase.button.delete') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<router-view
|
||||
v-if="track"
|
||||
:key="route.fullPath"
|
||||
:track="track"
|
||||
:object="track"
|
||||
object-type="track"
|
||||
@libraries-loaded="libraries = $event"
|
||||
/>
|
||||
</template>
|
||||
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
overflow: hidden;
|
||||
height: calc(2em - 4px);
|
||||
margin: 2px;
|
||||
align-content: center;
|
||||
|
||||
+ .pill-content {
|
||||
padding-left: 0.25em;
|
||||
|
@ -45,6 +46,10 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
> i.bi {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
> img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue