fix(front): consistent header on track detail

This commit is contained in:
ArneBo 2025-04-09 14:56:05 +02:00
parent 8d775fc5ed
commit 378455b0eb
2 changed files with 260 additions and 268 deletions

View File

@ -18,6 +18,7 @@ import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
import HumanDuration from '~/components/common/HumanDuration.vue'
import Layout from '~/components/ui/Layout.vue'
import Header from '~/components/ui/Header.vue'
import Loader from '~/components/ui/Loader.vue'
import Modal from '~/components/ui/Modal.vue'
import PlayButton from '~/components/audio/PlayButton.vue'
@ -153,283 +154,275 @@ watch(showDeleteModal, (newValue) => {
</script>
<template>
<Layout
stack
main
<Loader
v-if="isLoading"
v-title="labels.title"
/>
<Header
v-if="track"
:h1="track.title"
:action="{
text: labels.download,
to: downloadUrl,
solid: true,
primary: true,
icon: 'bi-download',
lowHeight: true
}"
page-heading
>
<Loader
v-if="isLoading"
v-title="labels.title"
<template #image>
<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"
>
</template>
<artist-credit-label
:artist-credit="track.artist_credit"
/>
<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"
>
<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"
/>
<human-duration
v-if="totalDuration > 0"
:duration="totalDuration"
/>
</div>
<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"
>
{{ labels.download }}
</Button>
</Layout>
<artist-credit-label
:artist-credit="track.artist_credit"
<Layout flex>
<PlayButton
:is-playable="track.is_playable"
class="vibrant"
split
:track="track"
low-height
/>
<Spacer
h
grow
/>
<TrackFavoriteIcon
v-if="store.state.auth.authenticated"
:track="track"
square-small
/>
<TrackPlaylistIcon
v-if="store.state.auth.authenticated"
:track="track"
square-small
/>
<Popover v-model="open">
<template #default="{ toggleOpen }">
<OptionsButton
is-square-small
@click="toggleOpen"
/>
<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"
/>
<human-duration
v-if="totalDuration > 0"
:duration="totalDuration"
/>
</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">
<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
gap-8
>
<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"
/>
<time
:title="track.creation_date"
:datetime="track.creation_date"
>
{{ momentFormat(new Date(track.creation_date), 'LL') }}
</time>
</Layout>
<Spacer :size="16" />
<Modal
v-if="isEmbedable"
v-model="showEmbedModal"
:title="t('components.library.TrackBase.modal.embed.header')"
>
<embed-wizard
:id="track.id"
type="track"
/>
<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"
<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.button.cancel') }}
</Button>
<Button
destructive
:is-loading="isLoading"
@click="remove()"
{{ 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') }}
</Button>
</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>
</Modal>
<router-view
v-if="track"
:key="route.fullPath"
:track="track"
:object="track"
object-type="track"
@libraries-loaded="libraries = $event"
/>
</template>
</Popover>
</Layout>
</Header>
<hr>
<Layout
flex
gap-8
>
<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"
/>
<time
:title="track?.creation_date"
:datetime="track?.creation_date"
>
{{ track?.creation_date ? momentFormat(new Date(track.creation_date), 'LL') : '' }}
</time>
</Layout>
<Spacer :size="64" />
<Modal
v-if="isEmbedable"
v-model="showEmbedModal"
:title="t('components.library.TrackBase.modal.embed.header')"
>
<embed-wizard
:id="track?.id || undefined"
type="track"
/>
<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>
<style scoped>
.meta {
line-height: 48px;
}
<style lang="scss" scoped>
.meta {
font-size: 15px;
line-height: 32px;
@include light-theme {
color: var(--fw-gray-700);
}
@include dark-theme {
color: var(--fw-gray-500);
}
}
</style>

View File

@ -147,7 +147,6 @@ const trackDetails: {
flex
style="gap: 24px;"
>
<Spacer />
<Layout
stack
style="flex: 1; gap: 0;"