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 type { ArtistCredit } from '~/types'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
||||||
import Link from '~/components/ui/Link.vue'
|
import Pill from '~/components/ui/Pill.vue'
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
||||||
|
@ -23,31 +23,28 @@ const getRoute = (ac: ArtistCredit) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="artist-label ui image label">
|
|
||||||
<template
|
<template
|
||||||
v-for="ac in props.artistCredit"
|
v-for="ac in props.artistCredit"
|
||||||
:key="ac.artist.id"
|
:key="ac.artist.id"
|
||||||
>
|
>
|
||||||
<Link
|
<router-link
|
||||||
solid
|
:to="{name: 'library.artists.detail', params: {id: ac.artist.id }}"
|
||||||
secondary
|
@click.stop.prevent=""
|
||||||
round
|
|
||||||
min-content
|
|
||||||
:to="getRoute(ac)"
|
|
||||||
>
|
>
|
||||||
|
<Pill>
|
||||||
|
<template #image>
|
||||||
<img
|
<img
|
||||||
v-if="ac.index === 0 && ac.artist.cover && ac.artist.cover.urls.original"
|
v-if="ac.artist.cover && ac.artist.cover.urls.original"
|
||||||
v-lazy="store.getters['instance/absoluteUrl'](ac.artist.cover.urls.medium_square_crop)"
|
v-lazy="store.getters['instance/absoluteUrl'](ac.artist.cover.urls.small_square_crop)"
|
||||||
alt=""
|
:alt="ac.artist.name"
|
||||||
:class="[{circular: ac.artist.content_category != 'podcast'}]"
|
/>
|
||||||
>
|
<i
|
||||||
<i
|
v-else
|
||||||
v-else-if="ac.index === 0"
|
class="bi bi-person-circle"
|
||||||
:class="[ac.artist.content_category != 'podcast' ? 'circular' : 'bordered', 'inverted violet bi bi-users icon']"
|
|
||||||
/>
|
/>
|
||||||
{{ ac.credit }}
|
|
||||||
</Link>
|
|
||||||
<span>{{ ac.joinphrase }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
{{ ac.credit }}
|
||||||
|
</Pill>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -69,7 +69,7 @@ const labels = computed(() => ({
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isShuffled,
|
isShuffled,
|
||||||
shuffle
|
shuffle,
|
||||||
} = useQueue()
|
} = useQueue()
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
@ -150,10 +150,7 @@ const remove = async () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout
|
<Layout stack main>
|
||||||
stack
|
|
||||||
main
|
|
||||||
>
|
|
||||||
<Loader
|
<Loader
|
||||||
v-if="isLoading"
|
v-if="isLoading"
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
|
@ -165,27 +162,15 @@ const remove = async () => {
|
||||||
v-lazy="store.getters['instance/absoluteUrl'](object.cover.urls.large_square_crop)"
|
v-lazy="store.getters['instance/absoluteUrl'](object.cover.urls.large_square_crop)"
|
||||||
:alt="object.title"
|
:alt="object.title"
|
||||||
class="channel-image"
|
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
|
<img
|
||||||
v-else
|
v-else
|
||||||
alt=""
|
alt=""
|
||||||
class="channel-image"
|
class="channel-image"
|
||||||
src="../../assets/audio/default-cover.png"
|
src="../../assets/audio/default-cover.png"
|
||||||
>
|
/>
|
||||||
<!-- ({target}) => target -->
|
<Layout stack style="flex: 1; gap: 8px;">
|
||||||
<!-- Header (TODO: Put into Header component) Hint: Header is heavier fontweight than h1! -->
|
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ object.title }}</h1>
|
||||||
<Layout
|
|
||||||
stack
|
|
||||||
style="flex: 1; gap: 8px;"
|
|
||||||
>
|
|
||||||
<h1>{{ object.title }}</h1>
|
|
||||||
<!-- <Header :h1="object.title" /> -->
|
|
||||||
<artist-credit-label
|
<artist-credit-label
|
||||||
v-if="artistCredit"
|
v-if="artistCredit"
|
||||||
:artist-credit="artistCredit"
|
:artist-credit="artistCredit"
|
||||||
|
@ -204,10 +189,7 @@ const remove = async () => {
|
||||||
{{ t('components.library.AlbumBase.meta.tracks', totalTracks) }}
|
{{ t('components.library.AlbumBase.meta.tracks', totalTracks) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<i
|
<i v-if="totalDuration > 0" class="bi bi-dot" />
|
||||||
v-if="totalDuration > 0"
|
|
||||||
class="bi bi-dot"
|
|
||||||
/>
|
|
||||||
<human-duration
|
<human-duration
|
||||||
v-if="totalDuration > 0"
|
v-if="totalDuration > 0"
|
||||||
:duration="totalDuration"
|
:duration="totalDuration"
|
||||||
|
@ -242,23 +224,14 @@ const remove = async () => {
|
||||||
artistCredit[0].artist.channel &&
|
artistCredit[0].artist.channel &&
|
||||||
artistCredit[0].artist.attributed_to?.full_username === store.state.auth.fullUsername"
|
artistCredit[0].artist.attributed_to?.full_username === store.state.auth.fullUsername"
|
||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
icon="bi-trash"
|
|
||||||
@confirm="remove()"
|
@confirm="remove()"
|
||||||
|
icon="bi-trash"
|
||||||
>
|
>
|
||||||
{{ t('components.library.AlbumDropdown.button.delete') }}
|
{{ t('components.library.AlbumDropdown.button.delete') }}
|
||||||
</DangerousButton>
|
</DangerousButton>
|
||||||
<Spacer
|
<Spacer h grow />
|
||||||
h
|
<TrackFavoriteIcon v-if="store.state.auth.authenticated" :album="object" />
|
||||||
grow
|
<TrackPlaylistIcon v-if="store.state.auth.authenticated" :album="object" />
|
||||||
/>
|
|
||||||
<TrackFavoriteIcon
|
|
||||||
v-if="store.state.auth.authenticated"
|
|
||||||
:album="object"
|
|
||||||
/>
|
|
||||||
<TrackPlaylistIcon
|
|
||||||
v-if="store.state.auth.authenticated"
|
|
||||||
:album="object"
|
|
||||||
/>
|
|
||||||
<!-- TODO: Share Button -->
|
<!-- TODO: Share Button -->
|
||||||
<album-dropdown
|
<album-dropdown
|
||||||
:object="object"
|
:object="object"
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Layout from '~/components/ui/Layout.vue'
|
||||||
import Modal from '~/components/ui/Modal.vue'
|
import Modal from '~/components/ui/Modal.vue'
|
||||||
import Spacer from '~/components/ui/Spacer.vue'
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
|
@ -55,6 +56,7 @@ const musicbrainzUrl = computed(() => object.value?.mbid ? `https://musicbrainz.
|
||||||
const discogsUrl = computed(() => `https://discogs.com/search/?type=artist&title=${encodeURI(object.value?.name ?? '')}`)
|
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 publicLibraries = computed(() => libraries.value?.filter(library => library.privacy_level === 'everyone') ?? [])
|
||||||
|
|
||||||
|
|
||||||
const cover = computed(() => {
|
const cover = computed(() => {
|
||||||
const artistCover: Cover | undefined = object.value?.cover
|
const artistCover: Cover | undefined = object.value?.cover
|
||||||
|
|
||||||
|
@ -120,11 +122,7 @@ watch(() => props.id, fetchData, { immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout
|
<Layout stack main v-title="labels.title">
|
||||||
v-title="labels.title"
|
|
||||||
stack
|
|
||||||
main
|
|
||||||
>
|
|
||||||
<Loader v-if="isLoading" />
|
<Loader v-if="isLoading" />
|
||||||
<template v-if="object && !isLoading">
|
<template v-if="object && !isLoading">
|
||||||
<Layout flex>
|
<Layout flex>
|
||||||
|
@ -133,16 +131,9 @@ watch(() => props.id, fetchData, { immediate: true })
|
||||||
:alt="object.name"
|
:alt="object.name"
|
||||||
class="channel-image"
|
class="channel-image"
|
||||||
>
|
>
|
||||||
<Layout
|
<Layout stack style="flex: 1; gap: 8px;">
|
||||||
stack
|
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ object.name }}</h1>
|
||||||
style="flex: 1; gap: 8px;"
|
<Layout flex class="meta" style="gap: 0;">
|
||||||
>
|
|
||||||
<h1>{{ object.name }}</h1>
|
|
||||||
<Layout
|
|
||||||
flex
|
|
||||||
class="meta"
|
|
||||||
style="gap: 0;"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-if="albums"
|
v-if="albums"
|
||||||
>
|
>
|
||||||
|
@ -190,8 +181,8 @@ watch(() => props.id, fetchData, { immediate: true })
|
||||||
|
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-if="publicLibraries.length > 0"
|
v-if="publicLibraries.length > 0"
|
||||||
icon="bi-code-square"
|
|
||||||
@click="showEmbedModal = true"
|
@click="showEmbedModal = true"
|
||||||
|
icon="bi-code-square"
|
||||||
>
|
>
|
||||||
{{ t('components.library.ArtistBase.button.embed') }}
|
{{ t('components.library.ArtistBase.button.embed') }}
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { useStore } from '~/store'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import ActorLink from '~/components/common/ActorLink.vue'
|
import ActorLink from '~/components/common/ActorLink.vue'
|
||||||
|
import ArtistCreditLabel from '~/components/audio/ArtistCreditLabel.vue'
|
||||||
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
||||||
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
||||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||||
|
@ -76,6 +77,8 @@ const attributedToUrl = computed(() => router.resolve({
|
||||||
}
|
}
|
||||||
})?.href)
|
})?.href)
|
||||||
|
|
||||||
|
const artistCredit = track.value?.artist_credit
|
||||||
|
|
||||||
const totalDuration = computed(() => track.value?.uploads[0]?.duration ?? 0)
|
const totalDuration = computed(() => track.value?.uploads[0]?.duration ?? 0)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
@ -126,10 +129,7 @@ watch(showDeleteModal, (newValue) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout
|
<Layout stack main>
|
||||||
stack
|
|
||||||
main
|
|
||||||
>
|
|
||||||
<Loader
|
<Loader
|
||||||
v-if="isLoading"
|
v-if="isLoading"
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
|
@ -155,16 +155,9 @@ watch(showDeleteModal, (newValue) => {
|
||||||
src="../../assets/audio/default-cover.png"
|
src="../../assets/audio/default-cover.png"
|
||||||
>
|
>
|
||||||
|
|
||||||
<Layout
|
<Layout stack style="flex: 1; gap: 8px;">
|
||||||
stack
|
<Layout flex no-gap style="align-items: baseline;">
|
||||||
style="flex: 1; gap: 8px;"
|
<h1 style="margin-top: 64px; margin-bottom: 8px;">{{ track.title }}</h1>
|
||||||
>
|
|
||||||
<Layout
|
|
||||||
flex
|
|
||||||
no-gap
|
|
||||||
style="align-items: baseline; margin-bottom: 24px;"
|
|
||||||
>
|
|
||||||
<h1>{{ track.title }}</h1>
|
|
||||||
<Spacer grow />
|
<Spacer grow />
|
||||||
<Button
|
<Button
|
||||||
v-if="upload"
|
v-if="upload"
|
||||||
|
@ -178,14 +171,14 @@ watch(showDeleteModal, (newValue) => {
|
||||||
{{ labels.download }}
|
{{ labels.download }}
|
||||||
</Button>
|
</Button>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
<artist-credit-label
|
||||||
|
:artist-credit="track.artist_credit"
|
||||||
|
/>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span>{{ t('components.library.TrackBase.title') }}</span>
|
<span>{{ t('components.library.TrackBase.title') }}</span>
|
||||||
<i class="bi bi-dot" />
|
<i class="bi bi-dot" />
|
||||||
<span>{{ track.album.title }}</span>
|
<span>{{ track.album.title }}</span>
|
||||||
<i
|
<i v-if="totalDuration > 0" class="bi bi-dot" />
|
||||||
v-if="totalDuration > 0"
|
|
||||||
class="bi bi-dot"
|
|
||||||
/>
|
|
||||||
<human-duration
|
<human-duration
|
||||||
v-if="totalDuration > 0"
|
v-if="totalDuration > 0"
|
||||||
:duration="totalDuration"
|
:duration="totalDuration"
|
||||||
|
@ -200,19 +193,10 @@ watch(showDeleteModal, (newValue) => {
|
||||||
:track="track"
|
:track="track"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacer
|
<Spacer h grow />
|
||||||
h
|
|
||||||
grow
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TrackFavoriteIcon
|
<TrackFavoriteIcon v-if="store.state.auth.authenticated" :track="track" />
|
||||||
v-if="store.state.auth.authenticated"
|
<TrackPlaylistIcon v-if="store.state.auth.authenticated" :track="track" />
|
||||||
:track="track"
|
|
||||||
/>
|
|
||||||
<TrackPlaylistIcon
|
|
||||||
v-if="store.state.auth.authenticated"
|
|
||||||
:track="track"
|
|
||||||
/>
|
|
||||||
<Popover v-model:open="open">
|
<Popover v-model:open="open">
|
||||||
<template #default="{ toggleOpen }">
|
<template #default="{ toggleOpen }">
|
||||||
<OptionsButton @click="toggleOpen" />
|
<OptionsButton @click="toggleOpen" />
|
||||||
|
@ -229,8 +213,8 @@ watch(showDeleteModal, (newValue) => {
|
||||||
|
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-if="isEmbedable"
|
v-if="isEmbedable"
|
||||||
icon="bi-code-slash"
|
|
||||||
@click="showEmbedModal = !showEmbedModal"
|
@click="showEmbedModal = !showEmbedModal"
|
||||||
|
icon="bi-code-slash"
|
||||||
>
|
>
|
||||||
{{ t('components.library.TrackBase.button.embed') }}
|
{{ t('components.library.TrackBase.button.embed') }}
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
@ -267,8 +251,8 @@ watch(showDeleteModal, (newValue) => {
|
||||||
store.state.auth.authenticated &&
|
store.state.auth.authenticated &&
|
||||||
artist.channel &&
|
artist.channel &&
|
||||||
artist.attributed_to.full_username === store.state.auth.fullUsername"
|
artist.attributed_to.full_username === store.state.auth.fullUsername"
|
||||||
icon="bi-trash"
|
|
||||||
@click="showDeleteModal = true"
|
@click="showDeleteModal = true"
|
||||||
|
icon="bi-trash"
|
||||||
>
|
>
|
||||||
{{ t('components.library.TrackBase.button.delete') }}
|
{{ t('components.library.TrackBase.button.delete') }}
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
@ -278,8 +262,8 @@ watch(showDeleteModal, (newValue) => {
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-for="obj in getReportableObjects({ track })"
|
v-for="obj in getReportableObjects({ track })"
|
||||||
:key="obj.target.type + obj.target.id"
|
:key="obj.target.type + obj.target.id"
|
||||||
icon="bi-flag"
|
|
||||||
@click="report(obj)"
|
@click="report(obj)"
|
||||||
|
icon="bi-flag"
|
||||||
>
|
>
|
||||||
{{ obj.label }}
|
{{ obj.label }}
|
||||||
</PopoverItem>
|
</PopoverItem>
|
||||||
|
@ -391,6 +375,7 @@ watch(showDeleteModal, (newValue) => {
|
||||||
@libraries-loaded="libraries = $event"
|
@libraries-loaded="libraries = $event"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: calc(2em - 4px);
|
height: calc(2em - 4px);
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
+ .pill-content {
|
+ .pill-content {
|
||||||
padding-left: 0.25em;
|
padding-left: 0.25em;
|
||||||
|
@ -45,6 +46,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> i.bi {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
> img {
|
> img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue