funkwhale/front/src/components/audio/track/Modal.vue

256 lines
8.4 KiB
Vue

<script setup lang="ts">
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport'
import { useStore } from '~/store'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { useVModel } from '@vueuse/core'
import { generateTrackCreditString, getArtistCoverUrl } from '~/utils/utils'
import Modal from '~/components/ui/Modal.vue'
interface Events {
(e: 'update:show', value: boolean): void
}
interface Props extends PlayOptionsProps {
track: Track
index: number
show: boolean
isArtist?: boolean
isAlbum?: boolean
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
isPlayable?: boolean
tracks?: Track[]
artist?: Artist | null
album?: Album | null
playlist?: Playlist | null
library?: Library | null
channel?: Channel | null
account?: Actor | null
}
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), {
isArtist: false,
isAlbum: false,
tracks: () => [],
artist: null,
album: null,
playlist: null,
library: null,
channel: null,
account: null
})
const modal = ref()
const show = useVModel(props, 'show', emit)
const { report, getReportableObjects } = useReport()
const { enqueue, enqueueNext } = usePlayOptions(props)
const store = useStore()
const isFavorite = computed(() => store.getters['favorites/isFavorite'](props.track.id))
const { t } = useI18n()
const router = useRouter()
const favoriteButton = computed(() => isFavorite.value
? t('components.audio.track.Modal.button.removeFromFavorites')
: t('components.audio.track.Modal.button.addToFavorites')
)
const trackDetailsButton = computed(() => props.track.artist_credit?.[0].artist.content_category === 'podcast'
? t('components.audio.track.Modal.button.episodeDetails')
: t('components.audio.track.Modal.button.trackDetails')
)
const albumDetailsButton = computed(() => props.track.artist_credit?.[0].artist?.content_category === 'podcast'
? t('components.audio.track.Modal.button.seriesDetails')
: t('components.audio.track.Modal.button.albumDetails')
)
const artistDetailsButton = computed(() => props.track.artist_credit?.[0].artist?.content_category === 'podcast'
? t('components.audio.track.Modal.button.channelDetails')
: t('components.audio.track.Modal.button.artistDetails')
)
const labels = computed(() => ({
startRadio: t('components.audio.track.Modal.button.startRadio'),
playNow: t('components.audio.track.Modal.button.playNow'),
addToQueue: t('components.audio.track.Modal.button.addToQueue'),
playNext: t('components.audio.track.Modal.button.playNext'),
addToPlaylist: t('components.audio.track.Modal.button.addToPlaylist')
}))
</script>
<template>
<Modal :title="track.title"
ref="modal"
v-model="show"
:scrolling="true"
class="scrolling-track-options"
>
<div class="header">
<div class="ui large centered rounded image">
<img
v-if="track.album?.cover?.urls.original"
v-lazy="store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)"
alt=""
class="ui centered image"
>
<img
v-else-if="track.cover"
v-lazy="store.getters['instance/absoluteUrl'](track.cover.urls.medium_square_crop)"
alt=""
class="ui centered image"
>
<img
v-else-if="!!track.artist_credit.length && track.artist_credit[0].artist.cover"
v-lazy="getArtistCoverUrl(track.artist_credit)"
alt=""
class="ui centered image"
>
<img
v-else
alt=""
class="ui centered image"
src="../../../assets/audio/default-cover.png"
>
</div>
<h4 class="track-modal-subtitle">
{{ generateTrackCreditString(track) }}
</h4>
</div>
<div class="ui hidden divider" />
<div class="content">
<div class="ui one column unstackable grid">
<div
v-if="store.state.auth.authenticated && track.artist_credit?.[0].artist.content_category !== 'podcast'"
class="row"
>
<div
tabindex="0"
class="column"
role="button"
:aria-label="favoriteButton"
@click.stop="store.dispatch('favorites/toggle', track.id)"
>
<i :class="[ 'heart', 'favorite-icon', { favorited: isFavorite, pink: isFavorite }, 'icon', 'track-modal', 'list-icon' ]" />
<span class="track-modal list-item">{{ favoriteButton }}</span>
</div>
</div>
<div class="row">
<div
class="column"
role="button"
:aria-label="labels.addToQueue"
@click.stop.prevent="enqueue(); show = false"
>
<i class="plus icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.addToQueue }}</span>
</div>
</div>
<div class="row">
<div
class="column"
role="button"
:aria-label="labels.playNext"
@click.stop.prevent="enqueueNext(true);show = false"
>
<i class="step forward icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.playNext }}</span>
</div>
</div>
<div class="row">
<div
class="column"
role="button"
:aria-label="labels.startRadio"
@click.stop.prevent="() => { store.dispatch('radios/start', { type: 'similar', objectId: track.id }); show = false }"
>
<i class="rss icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.startRadio }}</span>
</div>
</div>
<div class="row">
<div
class="column"
role="button"
:aria-label="labels.addToPlaylist"
@click.stop="store.commit('playlists/chooseTrack', track)"
>
<i class="list icon track-modal list-icon" />
<span class="track-modal list-item">
{{ labels.addToPlaylist }}
</span>
</div>
</div>
<div class="ui divider" />
<div
v-if="!isAlbum && track.album"
class="row"
>
<div
class="column"
role="button"
:aria-label="albumDetailsButton"
@click.prevent.exact="router.push({ name: 'library.albums.detail', params: { id: track.album?.id } })"
>
<i class="compact disc icon track-modal list-icon" />
<span class="track-modal list-item">{{ albumDetailsButton }}</span>
</div>
</div>
<div
v-if="!isArtist"
class="row"
>
<div
v-for="ac in track.artist_credit"
:key="ac.artist.id"
class="column"
role="button"
:aria-label="artistDetailsButton"
@click.prevent.exact="router.push({ name: 'library.artists.detail', params: { id: ac.artist.id } })"
>
<i class="user icon track-modal list-icon" />
<span class="track-modal list-item">{{ ac.credit }}</span>
<span v-if="ac.joinphrase">{{ ac.joinphrase }}</span>
</div>
</div>
<div class="row">
<div
class="column"
role="button"
:aria-label="trackDetailsButton"
@click.prevent.exact="router.push({ name: 'library.tracks.detail', params: { id: track.id } })"
>
<i class="info icon track-modal list-icon" />
<span class="track-modal list-item">{{ trackDetailsButton }}</span>
</div>
</div>
<div class="ui divider" />
<div
v-for="obj in getReportableObjects({ track, album: track.album, artistCredit: track.artist_credit })"
:key="obj.target.type + obj.target.id"
class="row"
@click.stop.prevent="report(obj)"
>
<div class="column">
<i class="share icon track-modal list-icon" />
<span class="track-modal list-item">{{ obj.label }}</span>
</div>
</div>
</div>
</div>
</Modal>
</template>