2022-07-02 23:49:07 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
import { useStore } from '~/store'
|
2022-07-21 15:25:22 +00:00
|
|
|
import { nextTick, onMounted, ref, computed, onBeforeMount, onUnmounted } from 'vue'
|
2022-07-02 23:49:07 +00:00
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
import time from '~/utils/time'
|
|
|
|
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
|
|
|
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
|
|
|
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
2022-07-03 23:57:14 +00:00
|
|
|
import Draggable from 'vuedraggable'
|
2022-07-02 23:49:07 +00:00
|
|
|
import { whenever, useTimeoutFn, useWindowScroll, useWindowSize } from '@vueuse/core'
|
2022-07-21 01:21:36 +00:00
|
|
|
import { useGettext } from 'vue3-gettext'
|
2022-07-03 21:36:27 +00:00
|
|
|
import useQueue from '~/composables/audio/useQueue'
|
|
|
|
import usePlayer from '~/composables/audio/usePlayer'
|
2022-07-02 23:49:07 +00:00
|
|
|
|
|
|
|
const queueModal = ref()
|
|
|
|
|
2022-07-21 15:25:22 +00:00
|
|
|
let savedScroll = 0
|
|
|
|
onBeforeMount(() => (savedScroll = window.scrollY))
|
|
|
|
onUnmounted(() => {
|
|
|
|
document.body.parentElement?.setAttribute('style', 'scroll-behavior: auto')
|
|
|
|
window.scrollTo({ top: savedScroll, behavior: undefined })
|
|
|
|
document.body.parentElement?.removeAttribute('style')
|
|
|
|
})
|
|
|
|
|
2022-07-02 23:49:07 +00:00
|
|
|
const { activate } = useFocusTrap(queueModal, { allowOutsideClick: true })
|
|
|
|
activate()
|
|
|
|
|
|
|
|
const store = useStore()
|
|
|
|
const currentIndex = computed(() => store.state.queue.currentIndex)
|
|
|
|
|
|
|
|
const { y: pageYOffset } = useWindowScroll()
|
|
|
|
const { height: windowHeight } = useWindowSize()
|
|
|
|
const scrollToCurrent = async () => {
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
const item = queueModal.value?.querySelector('.queue-item.active')
|
|
|
|
const { top } = item?.getBoundingClientRect() ?? { top: 0 }
|
|
|
|
|
|
|
|
window.scrollTo({
|
|
|
|
top: top + pageYOffset.value - windowHeight.value / 2,
|
|
|
|
behavior: 'smooth'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
await nextTick()
|
|
|
|
// NOTE: delay is to let transition work
|
|
|
|
useTimeoutFn(scrollToCurrent, 400)
|
|
|
|
})
|
|
|
|
|
|
|
|
const { $pgettext } = useGettext()
|
|
|
|
|
2022-07-21 01:21:36 +00:00
|
|
|
const {
|
|
|
|
playing,
|
2022-07-02 23:49:07 +00:00
|
|
|
loading: isLoadingAudio,
|
|
|
|
errored,
|
|
|
|
focused: playerFocused,
|
|
|
|
duration,
|
|
|
|
durationFormatted,
|
|
|
|
currentTimeFormatted,
|
|
|
|
progress,
|
|
|
|
bufferProgress,
|
2022-07-03 23:57:14 +00:00
|
|
|
currentTime,
|
2022-07-02 23:49:07 +00:00
|
|
|
pause,
|
2022-07-21 01:21:36 +00:00
|
|
|
resume
|
2022-07-02 23:49:07 +00:00
|
|
|
} = usePlayer()
|
|
|
|
|
2022-07-21 01:21:36 +00:00
|
|
|
const {
|
2022-07-02 23:49:07 +00:00
|
|
|
focused: queueFocused,
|
|
|
|
currentTrack,
|
|
|
|
hasNext,
|
|
|
|
isEmpty: emptyQueue,
|
|
|
|
tracks,
|
|
|
|
reorder: reorderTracks,
|
|
|
|
endsIn: timeLeft,
|
|
|
|
removeTrack,
|
|
|
|
clear,
|
|
|
|
next,
|
|
|
|
previous
|
|
|
|
} = useQueue()
|
|
|
|
|
|
|
|
const reorder = (event: { oldIndex: number, newIndex: number }) => reorderTracks(event.oldIndex, event.newIndex)
|
|
|
|
|
|
|
|
const labels = computed(() => ({
|
|
|
|
queue: $pgettext('*/*/*', 'Queue'),
|
|
|
|
duration: $pgettext('*/*/*', 'Duration'),
|
|
|
|
addArtistContentFilter: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Hide content from this artist…'),
|
|
|
|
restart: $pgettext('*/*/*', 'Restart track'),
|
|
|
|
previous: $pgettext('*/*/*', 'Previous track'),
|
|
|
|
next: $pgettext('*/*/*', 'Next track'),
|
|
|
|
pause: $pgettext('*/*/*', 'Pause'),
|
|
|
|
play: $pgettext('*/*/*', 'Play'),
|
|
|
|
remove: $pgettext('*/*/*', 'Remove'),
|
|
|
|
selectTrack: $pgettext('*/*/*', 'Select track')
|
|
|
|
}))
|
|
|
|
|
|
|
|
whenever(queueFocused, scrollToCurrent, { immediate: true })
|
|
|
|
whenever(currentTrack, scrollToCurrent, { immediate: true })
|
|
|
|
|
|
|
|
whenever(
|
|
|
|
() => tracks.value.length === 0,
|
|
|
|
() => store.commit('ui/queueFocused', null),
|
|
|
|
{ immediate: true }
|
|
|
|
)
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
router.beforeEach(() => store.commit('ui/queueFocused', null))
|
|
|
|
|
|
|
|
const progressBar = ref()
|
|
|
|
const touchProgress = (event: MouseEvent) => {
|
2022-07-03 23:57:14 +00:00
|
|
|
const time = ((event.clientX - (event.target as Element).getBoundingClientRect().left) / progressBar.value.offsetWidth) * duration.value
|
|
|
|
currentTime.value = time
|
2022-07-02 23:49:07 +00:00
|
|
|
}
|
2022-07-21 23:28:54 +00:00
|
|
|
|
|
|
|
const play = (index: number) => {
|
|
|
|
store.dispatch('queue/currentIndex', index)
|
|
|
|
resume()
|
|
|
|
}
|
2022-07-02 23:49:07 +00:00
|
|
|
</script>
|
|
|
|
|
2019-12-26 10:38:26 +00:00
|
|
|
<template>
|
2021-12-06 10:35:20 +00:00
|
|
|
<section
|
2022-05-01 09:19:34 +00:00
|
|
|
ref="queueModal"
|
2021-12-06 10:35:20 +00:00
|
|
|
class="main with-background component-queue"
|
|
|
|
:aria-label="labels.queue"
|
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
<div :class="['ui vertical stripe queue segment', playerFocused ? 'player-focused' : '']">
|
|
|
|
<div class="ui fluid container">
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
id="queue-grid"
|
|
|
|
class="ui stackable grid"
|
|
|
|
>
|
2020-05-15 12:12:36 +00:00
|
|
|
<div class="ui six wide column current-track">
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
id="player"
|
|
|
|
class="ui basic segment"
|
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
<template v-if="currentTrack">
|
2021-12-06 10:35:20 +00:00
|
|
|
<img
|
|
|
|
v-if="currentTrack.cover && currentTrack.cover.urls.large_square_crop"
|
|
|
|
ref="cover"
|
|
|
|
alt=""
|
|
|
|
:src="$store.getters['instance/absoluteUrl'](currentTrack.cover.urls.large_square_crop)"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
v-else-if="currentTrack.album && currentTrack.album.cover && currentTrack.album.cover.urls.large_square_crop"
|
|
|
|
ref="cover"
|
|
|
|
alt=""
|
|
|
|
:src="$store.getters['instance/absoluteUrl'](currentTrack.album.cover.urls.large_square_crop)"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
v-else
|
|
|
|
class="ui image"
|
|
|
|
alt=""
|
|
|
|
src="../assets/audio/default-cover.png"
|
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
<h1 class="ui header">
|
2020-07-27 09:05:34 +00:00
|
|
|
<div class="content ellipsis">
|
2021-12-06 10:35:20 +00:00
|
|
|
<router-link
|
|
|
|
class="small header discrete link track"
|
|
|
|
:to="{name: 'library.tracks.detail', params: {id: currentTrack.id }}"
|
|
|
|
>
|
2020-07-27 09:05:34 +00:00
|
|
|
{{ currentTrack.title }}
|
2019-12-26 10:38:26 +00:00
|
|
|
</router-link>
|
2020-07-27 09:05:34 +00:00
|
|
|
<div class="sub header ellipsis">
|
2021-12-06 10:35:20 +00:00
|
|
|
<router-link
|
|
|
|
class="discrete link artist"
|
|
|
|
:to="{name: 'library.artists.detail', params: {id: currentTrack.artist.id }}"
|
|
|
|
>
|
|
|
|
{{ currentTrack.artist.name }}
|
|
|
|
</router-link>
|
|
|
|
<template v-if="currentTrack.album">
|
|
|
|
/
|
|
|
|
<router-link
|
|
|
|
class="discrete link album"
|
|
|
|
:to="{name: 'library.albums.detail', params: {id: currentTrack.album.id }}"
|
|
|
|
>
|
|
|
|
{{ currentTrack.album.title }}
|
|
|
|
</router-link>
|
2021-01-03 16:26:09 +00:00
|
|
|
</template>
|
2019-12-26 10:38:26 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</h1>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-if="currentTrack && errored"
|
|
|
|
class="ui small warning message"
|
|
|
|
>
|
2020-07-03 12:20:47 +00:00
|
|
|
<h3 class="header">
|
2021-12-06 10:35:20 +00:00
|
|
|
<translate translate-context="Sidebar/Player/Error message.Title">
|
|
|
|
The track cannot be loaded
|
|
|
|
</translate>
|
2020-07-03 12:20:47 +00:00
|
|
|
</h3>
|
2019-12-26 10:38:26 +00:00
|
|
|
<p v-if="hasNext && playing && $store.state.player.errorCount < $store.state.player.maxConsecutiveErrors">
|
2021-12-06 10:35:20 +00:00
|
|
|
<translate translate-context="Sidebar/Player/Error message.Paragraph">
|
|
|
|
The next track will play automatically in a few seconds…
|
|
|
|
</translate>
|
|
|
|
<i class="loading spinner icon" />
|
2019-12-26 10:38:26 +00:00
|
|
|
</p>
|
|
|
|
<p>
|
2021-12-06 10:35:20 +00:00
|
|
|
<translate translate-context="Sidebar/Player/Error message.Paragraph">
|
|
|
|
You may have a connectivity issue.
|
|
|
|
</translate>
|
2019-12-26 10:38:26 +00:00
|
|
|
</p>
|
|
|
|
</div>
|
2020-08-31 15:16:48 +00:00
|
|
|
<div class="additional-controls tablet-and-below">
|
2019-12-26 10:38:26 +00:00
|
|
|
<track-favorite-icon
|
|
|
|
v-if="$store.state.auth.authenticated"
|
2021-12-06 10:35:20 +00:00
|
|
|
:track="currentTrack"
|
|
|
|
/>
|
2019-12-26 10:38:26 +00:00
|
|
|
<track-playlist-icon
|
|
|
|
v-if="$store.state.auth.authenticated"
|
2021-12-06 10:35:20 +00:00
|
|
|
:track="currentTrack"
|
|
|
|
/>
|
2019-12-26 10:38:26 +00:00
|
|
|
<button
|
|
|
|
v-if="$store.state.auth.authenticated"
|
2020-08-31 15:16:48 +00:00
|
|
|
:class="['ui', 'really', 'basic', 'circular', 'icon', 'button']"
|
2019-12-26 10:38:26 +00:00
|
|
|
:aria-label="labels.addArtistContentFilter"
|
2021-12-06 10:35:20 +00:00
|
|
|
:title="labels.addArtistContentFilter"
|
|
|
|
@click="$store.dispatch('moderation/hide', {type: 'artist', target: currentTrack.artist})"
|
|
|
|
>
|
|
|
|
<i :class="['eye slash outline', 'basic', 'icon']" />
|
2019-12-26 10:38:26 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="progress-wrapper">
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-if="currentTrack && !errored"
|
|
|
|
class="progress-area"
|
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
<div
|
2022-07-02 23:49:07 +00:00
|
|
|
ref="progressBar"
|
2020-05-15 12:12:36 +00:00
|
|
|
:class="['ui', 'small', 'vibrant', {'indicating': isLoadingAudio}, 'progress']"
|
2021-12-06 10:35:20 +00:00
|
|
|
@click="touchProgress"
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
class="buffer bar"
|
2022-07-21 21:46:23 +00:00
|
|
|
:style="{ 'transform': `translateX(${bufferProgress - 100}%)` }"
|
2021-12-06 10:35:20 +00:00
|
|
|
/>
|
|
|
|
<div
|
|
|
|
class="position bar"
|
2022-07-21 21:46:23 +00:00
|
|
|
:style="{ 'transform': `translateX(${progress - 100}%)` }"
|
2021-12-06 10:35:20 +00:00
|
|
|
/>
|
2019-12-26 10:38:26 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-else
|
|
|
|
class="progress-area"
|
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
<div
|
|
|
|
ref="progress"
|
2021-12-06 10:35:20 +00:00
|
|
|
:class="['ui', 'small', 'vibrant', 'progress']"
|
|
|
|
>
|
|
|
|
<div class="buffer bar" />
|
|
|
|
<div class="position bar" />
|
2019-12-26 10:38:26 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="progress">
|
|
|
|
<template v-if="!isLoadingAudio">
|
2021-12-06 10:35:20 +00:00
|
|
|
<a
|
|
|
|
href=""
|
|
|
|
:aria-label="labels.restart"
|
|
|
|
class="left floated timer discrete start"
|
2022-07-03 23:57:14 +00:00
|
|
|
@click.prevent="currentTime = 0"
|
2021-12-06 10:35:20 +00:00
|
|
|
>{{ currentTimeFormatted }}</a>
|
|
|
|
<span class="right floated timer total">{{ durationFormatted }}</span>
|
2019-12-26 10:38:26 +00:00
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<span class="left floated timer">00:00</span>
|
|
|
|
<span class="right floated timer">00:00</span>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="player-controls tablet-and-below">
|
2021-12-06 10:35:20 +00:00
|
|
|
<span
|
|
|
|
role="button"
|
2022-07-02 23:49:07 +00:00
|
|
|
:title="labels.previous"
|
|
|
|
:aria-label="labels.previous"
|
2021-12-06 10:35:20 +00:00
|
|
|
class="control"
|
2022-05-01 15:36:36 +00:00
|
|
|
:disabled="emptyQueue || null"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click.prevent.stop="previous"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<i :class="['ui', 'backward step', {'disabled': emptyQueue}, 'icon']" />
|
|
|
|
</span>
|
2019-12-26 10:38:26 +00:00
|
|
|
|
2021-12-06 10:35:20 +00:00
|
|
|
<span
|
|
|
|
v-if="!playing"
|
|
|
|
role="button"
|
|
|
|
:title="labels.play"
|
|
|
|
:aria-label="labels.play"
|
|
|
|
class="control"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click.prevent.stop="resume"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<i :class="['ui', 'play', {'disabled': !currentTrack}, 'icon']" />
|
|
|
|
</span>
|
|
|
|
<span
|
|
|
|
v-else
|
|
|
|
role="button"
|
|
|
|
:title="labels.pause"
|
|
|
|
:aria-label="labels.pause"
|
|
|
|
class="control"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click.prevent.stop="pause"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<i :class="['ui', 'pause', {'disabled': !currentTrack}, 'icon']" />
|
|
|
|
</span>
|
|
|
|
<span
|
|
|
|
role="button"
|
|
|
|
:title="labels.next"
|
|
|
|
:aria-label="labels.next"
|
|
|
|
class="control"
|
2022-05-01 15:36:36 +00:00
|
|
|
:disabled="hasNext || null"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click.prevent.stop="next"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<i :class="['ui', {'disabled': !hasNext}, 'forward step', 'icon']" />
|
|
|
|
</span>
|
2019-12-26 10:38:26 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-05-15 12:12:36 +00:00
|
|
|
<div class="ui ten wide column queue-column">
|
2019-12-26 10:38:26 +00:00
|
|
|
<div class="ui basic clearing fixed-header segment">
|
|
|
|
<h2 class="ui header">
|
|
|
|
<div class="content">
|
|
|
|
<button
|
2021-01-03 16:47:44 +00:00
|
|
|
class="ui right floated basic button"
|
2021-12-06 10:35:20 +00:00
|
|
|
@click="$store.commit('ui/queueFocused', null)"
|
|
|
|
>
|
|
|
|
<translate translate-context="*/Queue/*/Verb">
|
|
|
|
Close
|
|
|
|
</translate>
|
2021-01-03 16:47:44 +00:00
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
class="ui right floated basic button danger"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click="clear"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
|
|
|
<translate translate-context="*/Queue/*/Verb">
|
|
|
|
Clear
|
|
|
|
</translate>
|
2019-12-26 10:38:26 +00:00
|
|
|
</button>
|
|
|
|
{{ labels.queue }}
|
|
|
|
<div class="sub header">
|
|
|
|
<div>
|
2021-12-06 10:35:20 +00:00
|
|
|
<translate
|
|
|
|
translate-context="Sidebar/Queue/Text"
|
2022-07-03 23:57:14 +00:00
|
|
|
:translate-params="{index: currentIndex + 1, length: tracks.length}"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
2019-12-26 10:38:26 +00:00
|
|
|
Track %{ index } of %{ length }
|
2022-07-02 23:49:07 +00:00
|
|
|
</translate>
|
|
|
|
<template v-if="!$store.state.radios.running">
|
2021-12-06 10:35:20 +00:00
|
|
|
-
|
2019-12-26 10:38:26 +00:00
|
|
|
<span :title="labels.duration">
|
|
|
|
{{ timeLeft }}
|
|
|
|
</span>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</h2>
|
|
|
|
</div>
|
|
|
|
<table class="ui compact very basic fixed single line selectable unstackable table">
|
2021-12-06 10:35:20 +00:00
|
|
|
<draggable
|
2022-07-03 23:57:14 +00:00
|
|
|
v-model="tracks"
|
2021-12-06 10:35:20 +00:00
|
|
|
tag="tbody"
|
|
|
|
handle=".handle"
|
2022-05-01 08:02:20 +00:00
|
|
|
item-key="id"
|
2022-05-01 09:19:34 +00:00
|
|
|
@update="reorder"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
2022-05-01 08:02:20 +00:00
|
|
|
<template #item="{ element: track, index }">
|
|
|
|
<tr
|
|
|
|
:key="track.id"
|
2022-05-01 09:19:34 +00:00
|
|
|
:class="['queue-item', {'active': index === currentIndex}]"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
2022-05-01 08:02:20 +00:00
|
|
|
<td class="handle">
|
|
|
|
<i class="grip lines icon" />
|
|
|
|
</td>
|
|
|
|
<td
|
|
|
|
class="image-cell"
|
2022-07-21 23:28:54 +00:00
|
|
|
@click="play(index)"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
2022-05-01 08:02:20 +00:00
|
|
|
<img
|
|
|
|
v-if="track.cover && track.cover.urls.original"
|
|
|
|
class="ui mini image"
|
|
|
|
alt=""
|
|
|
|
:src="$store.getters['instance/absoluteUrl'](track.cover.urls.medium_square_crop)"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
v-else-if="track.album && track.album.cover && track.album.cover.urls.original"
|
|
|
|
class="ui mini image"
|
|
|
|
alt=""
|
|
|
|
:src="$store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
v-else
|
|
|
|
class="ui mini image"
|
|
|
|
alt=""
|
|
|
|
src="../assets/audio/default-cover.png"
|
|
|
|
>
|
|
|
|
</td>
|
|
|
|
<td
|
|
|
|
colspan="3"
|
2022-07-21 23:28:54 +00:00
|
|
|
@click="play(index)"
|
2021-12-06 10:35:20 +00:00
|
|
|
>
|
2022-05-01 08:02:20 +00:00
|
|
|
<button
|
|
|
|
class="title reset ellipsis"
|
|
|
|
:title="track.title"
|
|
|
|
:aria-label="labels.selectTrack"
|
|
|
|
>
|
|
|
|
<strong>{{ track.title }}</strong><br>
|
|
|
|
<span>
|
2022-05-01 09:19:34 +00:00
|
|
|
{{ track.artist.name }}
|
|
|
|
</span>
|
2022-05-01 08:02:20 +00:00
|
|
|
</button>
|
|
|
|
</td>
|
|
|
|
<td class="duration-cell">
|
|
|
|
<template v-if="track.uploads.length > 0">
|
|
|
|
{{ time.durationFormatted(track.uploads[0].duration) }}
|
|
|
|
</template>
|
|
|
|
</td>
|
|
|
|
<td class="controls">
|
|
|
|
<template v-if="$store.getters['favorites/isFavorite'](track.id)">
|
|
|
|
<i class="pink heart icon" />
|
|
|
|
</template>
|
|
|
|
<button
|
2022-07-02 23:49:07 +00:00
|
|
|
:aria-label="labels.remove"
|
|
|
|
:title="labels.remove"
|
2022-05-01 08:02:20 +00:00
|
|
|
:class="['ui', 'really', 'tiny', 'basic', 'circular', 'icon', 'button']"
|
2022-07-02 23:49:07 +00:00
|
|
|
@click.stop="removeTrack(index)"
|
2022-05-01 08:02:20 +00:00
|
|
|
>
|
|
|
|
<i class="x icon" />
|
|
|
|
</button>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</template>
|
2019-12-26 10:38:26 +00:00
|
|
|
</draggable>
|
|
|
|
</table>
|
2020-03-26 08:44:31 +00:00
|
|
|
|
2021-12-06 10:35:20 +00:00
|
|
|
<div
|
|
|
|
v-if="$store.state.radios.running"
|
|
|
|
class="ui info message"
|
|
|
|
>
|
2020-03-26 08:44:31 +00:00
|
|
|
<div class="content">
|
2020-07-03 12:20:47 +00:00
|
|
|
<h3 class="header">
|
2021-12-06 10:35:20 +00:00
|
|
|
<i class="feed icon" /> <translate translate-context="Sidebar/Player/Title">
|
|
|
|
You have a radio playing
|
|
|
|
</translate>
|
2020-07-03 12:20:47 +00:00
|
|
|
</h3>
|
2021-12-06 10:35:20 +00:00
|
|
|
<p>
|
|
|
|
<translate translate-context="Sidebar/Player/Paragraph">
|
|
|
|
New tracks will be appended here automatically.
|
|
|
|
</translate>
|
|
|
|
</p>
|
|
|
|
<button
|
|
|
|
class="ui basic primary button"
|
|
|
|
@click="$store.dispatch('radios/stop')"
|
|
|
|
>
|
|
|
|
<translate translate-context="*/Player/Button.Label/Short, Verb">
|
|
|
|
Stop radio
|
|
|
|
</translate>
|
|
|
|
</button>
|
2020-03-26 08:44:31 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2019-12-26 10:38:26 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</template>
|