2022-07-05 22:34:11 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
import type { PlaylistTrack, Playlist } from '~/types'
|
|
|
|
|
2022-09-08 14:32:45 +00:00
|
|
|
import { useI18n } from 'vue-i18n'
|
2022-08-09 01:08:31 +00:00
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
import { useStore } from '~/store'
|
|
|
|
|
2022-07-05 22:34:11 +00:00
|
|
|
import axios from 'axios'
|
2022-08-09 01:08:31 +00:00
|
|
|
|
2022-07-05 22:34:11 +00:00
|
|
|
import PlaylistEditor from '~/components/playlists/Editor.vue'
|
|
|
|
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
2022-07-17 11:19:46 +00:00
|
|
|
import SemanticModal from '~/components/semantic/Modal.vue'
|
2022-08-09 01:08:31 +00:00
|
|
|
import TrackTable from '~/components/audio/track/Table.vue'
|
|
|
|
import PlayButton from '~/components/audio/PlayButton.vue'
|
|
|
|
|
|
|
|
import useErrorHandler from '~/composables/useErrorHandler'
|
2022-07-05 22:34:11 +00:00
|
|
|
|
|
|
|
interface Props {
|
2022-08-31 16:39:24 +00:00
|
|
|
id: number
|
2022-07-05 22:34:11 +00:00
|
|
|
defaultEdit?: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
defaultEdit: false
|
|
|
|
})
|
|
|
|
|
|
|
|
const store = useStore()
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
const edit = ref(props.defaultEdit)
|
|
|
|
const playlist = ref<Playlist | null>(null)
|
|
|
|
const playlistTracks = ref<PlaylistTrack[]>([])
|
|
|
|
|
|
|
|
const showEmbedModal = ref(false)
|
|
|
|
|
|
|
|
const tracks = computed(() => playlistTracks.value.map(({ track }, index) => ({ ...track, position: index + 1 })))
|
|
|
|
|
2022-09-08 14:32:45 +00:00
|
|
|
const { t } = useI18n()
|
2022-07-05 22:34:11 +00:00
|
|
|
const labels = computed(() => ({
|
2022-09-18 19:14:35 +00:00
|
|
|
playlist: t('views.playlists.Detail.title')
|
2022-07-05 22:34:11 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
const isLoading = ref(false)
|
|
|
|
const fetchData = async () => {
|
|
|
|
isLoading.value = true
|
|
|
|
|
|
|
|
try {
|
|
|
|
const [playlistResponse, tracksResponse] = await Promise.all([
|
|
|
|
axios.get(`playlists/${props.id}/`),
|
2022-07-21 01:21:36 +00:00
|
|
|
axios.get(`playlists/${props.id}/tracks/`)
|
2022-07-05 22:34:11 +00:00
|
|
|
])
|
|
|
|
|
|
|
|
playlist.value = playlistResponse.data
|
|
|
|
playlistTracks.value = tracksResponse.data.results
|
|
|
|
} catch (error) {
|
2022-08-09 01:08:31 +00:00
|
|
|
useErrorHandler(error as Error)
|
2022-07-05 22:34:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isLoading.value = false
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchData()
|
|
|
|
|
|
|
|
const deletePlaylist = async () => {
|
|
|
|
try {
|
|
|
|
await axios.delete(`playlists/${props.id}/`)
|
|
|
|
store.dispatch('playlists/fetchOwn')
|
|
|
|
return router.push({ path: '/library' })
|
|
|
|
} catch (error) {
|
2022-08-09 01:08:31 +00:00
|
|
|
useErrorHandler(error as Error)
|
2022-07-05 22:34:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
2018-03-20 18:58:27 +00:00
|
|
|
<template>
|
2018-11-19 22:33:22 +00:00
|
|
|
<main>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-if="isLoading"
|
|
|
|
v-title="labels.playlist"
|
|
|
|
class="ui vertical segment"
|
|
|
|
>
|
|
|
|
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
|
2018-03-20 22:41:15 +00:00
|
|
|
</div>
|
2021-12-06 10:35:20 +00:00
|
|
|
<section
|
|
|
|
v-if="!isLoading && playlist"
|
|
|
|
v-title="playlist.name"
|
|
|
|
class="ui head vertical center aligned stripe segment"
|
|
|
|
>
|
2018-03-20 18:58:27 +00:00
|
|
|
<div class="segment-content">
|
|
|
|
<h2 class="ui center aligned icon header">
|
2021-12-06 10:35:20 +00:00
|
|
|
<i class="circular inverted list warning icon" />
|
2018-03-20 18:58:27 +00:00
|
|
|
<div class="content">
|
|
|
|
{{ playlist.name }}
|
2018-07-17 11:09:13 +00:00
|
|
|
<div class="sub header">
|
2022-11-27 12:15:43 +00:00
|
|
|
{{ $t('views.playlists.Detail.meta.tracks', {count: playlist.tracks_count, username: playlist.user.username}) }}
|
2022-09-18 19:14:35 +00:00
|
|
|
<br>
|
2018-07-17 11:09:13 +00:00
|
|
|
<duration :seconds="playlist.duration" />
|
|
|
|
</div>
|
2018-03-20 18:58:27 +00:00
|
|
|
</div>
|
|
|
|
</h2>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div class="ui hidden divider" />
|
2021-02-20 21:45:18 +00:00
|
|
|
<div class="header-buttons">
|
|
|
|
<div class="ui buttons">
|
2021-12-06 10:35:20 +00:00
|
|
|
<play-button
|
|
|
|
class="vibrant"
|
|
|
|
:is-playable="playlist.is_playable"
|
|
|
|
:tracks="tracks"
|
|
|
|
>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.playAll') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</play-button>
|
2021-02-20 21:45:18 +00:00
|
|
|
</div>
|
|
|
|
<div class="ui buttons">
|
|
|
|
<button
|
2022-07-05 22:34:11 +00:00
|
|
|
v-if="$store.state.auth.profile && playlist.user.id === $store.state.auth.profile?.id"
|
2021-12-06 10:35:20 +00:00
|
|
|
class="ui icon labeled button"
|
|
|
|
@click="edit = !edit"
|
|
|
|
>
|
|
|
|
<i class="pencil icon" />
|
|
|
|
<template v-if="edit">
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.stopEdit') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</template>
|
|
|
|
<template v-else>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.edit') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</template>
|
2021-02-20 21:45:18 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div class="ui buttons">
|
2021-02-20 21:45:18 +00:00
|
|
|
<button
|
|
|
|
v-if="playlist.privacy_level === 'everyone' && playlist.is_playable"
|
2021-12-06 10:35:20 +00:00
|
|
|
class="ui icon labeled button"
|
|
|
|
@click="showEmbedModal = !showEmbedModal"
|
|
|
|
>
|
|
|
|
<i class="code icon" />
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.embed') }}
|
2021-02-20 21:45:18 +00:00
|
|
|
</button>
|
2021-12-06 10:35:20 +00:00
|
|
|
<dangerous-button
|
|
|
|
v-if="$store.state.auth.profile && playlist.user.id === $store.state.auth.profile.id"
|
|
|
|
class="ui labeled danger icon button"
|
|
|
|
:action="deletePlaylist"
|
|
|
|
>
|
2022-09-18 19:14:35 +00:00
|
|
|
<i class="trash icon" />
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.delete') }}
|
2022-05-01 14:15:57 +00:00
|
|
|
<template #modal-header>
|
2022-09-18 19:14:35 +00:00
|
|
|
<p>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.modal.delete.header', {playlist: playlist.name}) }}
|
2022-05-01 14:15:57 +00:00
|
|
|
</p>
|
|
|
|
</template>
|
|
|
|
<template #modal-content>
|
|
|
|
<p>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.modal.delete.content.warning') }}
|
2022-05-01 14:15:57 +00:00
|
|
|
</p>
|
|
|
|
</template>
|
|
|
|
<template #modal-confirm>
|
|
|
|
<div>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.confirm') }}
|
2022-05-01 14:15:57 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
2021-02-20 21:45:18 +00:00
|
|
|
</dangerous-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-17 11:19:46 +00:00
|
|
|
<semantic-modal
|
2021-12-06 10:35:20 +00:00
|
|
|
v-if="playlist.privacy_level === 'everyone' && playlist.is_playable"
|
2022-05-01 11:45:07 +00:00
|
|
|
v-model:show="showEmbedModal"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<h4 class="header">
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.modal.embed.header') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</h4>
|
|
|
|
<div class="scrolling content">
|
|
|
|
<div class="description">
|
|
|
|
<embed-wizard
|
|
|
|
:id="playlist.id"
|
|
|
|
type="playlist"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="actions">
|
|
|
|
<button class="ui basic deny button">
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.cancel') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</button>
|
2019-09-12 07:48:28 +00:00
|
|
|
</div>
|
2022-07-17 11:19:46 +00:00
|
|
|
</semantic-modal>
|
2021-02-20 21:45:18 +00:00
|
|
|
</div>
|
2018-11-19 22:33:22 +00:00
|
|
|
</section>
|
|
|
|
<section class="ui vertical stripe segment">
|
2018-03-20 18:58:27 +00:00
|
|
|
<template v-if="edit">
|
2018-03-21 18:04:15 +00:00
|
|
|
<playlist-editor
|
2022-07-05 22:34:11 +00:00
|
|
|
v-model:playlist="playlist"
|
|
|
|
v-model:playlist-tracks="playlistTracks"
|
2021-12-06 10:35:20 +00:00
|
|
|
/>
|
2018-03-20 18:58:27 +00:00
|
|
|
</template>
|
2019-10-17 12:15:33 +00:00
|
|
|
<template v-else-if="tracks.length > 0">
|
2021-12-06 10:35:20 +00:00
|
|
|
<h2>
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.header.tracks') }}
|
2021-12-06 10:35:20 +00:00
|
|
|
</h2>
|
|
|
|
<track-table
|
|
|
|
:display-position="true"
|
|
|
|
:tracks="tracks"
|
2022-07-17 13:18:55 +00:00
|
|
|
:unique="false"
|
2021-12-06 10:35:20 +00:00
|
|
|
/>
|
2018-03-20 18:58:27 +00:00
|
|
|
</template>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-else
|
|
|
|
class="ui placeholder segment"
|
|
|
|
>
|
2019-10-17 12:15:33 +00:00
|
|
|
<div class="ui icon header">
|
2021-12-06 10:35:20 +00:00
|
|
|
<i class="list icon" />
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.empty.noTracks') }}
|
2019-10-17 12:15:33 +00:00
|
|
|
</div>
|
2021-12-06 10:35:20 +00:00
|
|
|
<button
|
|
|
|
class="ui success icon labeled button"
|
|
|
|
@click="edit = !edit"
|
|
|
|
>
|
|
|
|
<i class="pencil icon" />
|
2022-09-23 18:14:25 +00:00
|
|
|
{{ $t('views.playlists.Detail.button.edit') }}
|
2019-10-17 12:15:33 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
2018-11-19 22:33:22 +00:00
|
|
|
</section>
|
|
|
|
</main>
|
2018-03-20 18:58:27 +00:00
|
|
|
</template>
|