mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-06 20:33:10 -05:00
[web] Switch to camel case
This commit is contained in:
@@ -1,75 +1,48 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div v-if="!item.isItem" class="py-5">
|
||||
<span
|
||||
:id="`index_${item.index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="item.index"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item.item)"
|
||||
>
|
||||
<control-image
|
||||
v-if="settingsStore.show_cover_artwork_in_album_lists"
|
||||
:url="item.item.artwork_url"
|
||||
:artist="item.item.artist"
|
||||
:album="item.item.name"
|
||||
class="media-left is-small"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
||||
<div
|
||||
class="is-size-7 has-text-grey has-text-weight-bold"
|
||||
v-text="item.item.artist"
|
||||
/>
|
||||
<div
|
||||
v-if="item.item.date_released && item.item.media_kind === 'music'"
|
||||
class="is-size-7 has-text-grey"
|
||||
v-text="$filters.toDate(item.item.date_released)"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-album
|
||||
:item="selectedItem"
|
||||
:media_kind="media_kind"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
@remove-podcast="openRemovePodcastDialog()"
|
||||
@play-count-changed="onPlayCountChange()"
|
||||
/>
|
||||
<modal-dialog
|
||||
:actions="actions"
|
||||
:show="showRemovePodcastModal"
|
||||
:title="$t('page.podcast.remove-podcast')"
|
||||
@cancel="showRemovePodcastModal = false"
|
||||
@remove="removePodcast"
|
||||
>
|
||||
<template #content>
|
||||
<i18n-t keypath="list.albums.info" tag="p" scope="global">
|
||||
<template #separator>
|
||||
<br />
|
||||
</template>
|
||||
<template #name>
|
||||
<b v-text="rss_playlist_to_remove.name" />
|
||||
</template>
|
||||
</i18n-t>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:is-item="item.isItem"
|
||||
:image="url(item)"
|
||||
:index="item.index"
|
||||
:lines="[
|
||||
item.item.name,
|
||||
item.item.artist,
|
||||
$filters.toDate(item.item.date_released)
|
||||
]"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-album
|
||||
:item="selectedItem"
|
||||
:media_kind="media_kind"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
@remove-podcast="openRemovePodcastDialog()"
|
||||
@play-count-changed="onPlayCountChange()"
|
||||
/>
|
||||
<modal-dialog
|
||||
:actions="actions"
|
||||
:show="showRemovePodcastModal"
|
||||
:title="$t('page.podcast.remove-podcast')"
|
||||
@cancel="showRemovePodcastModal = false"
|
||||
@remove="removePodcast"
|
||||
>
|
||||
<template #content>
|
||||
<i18n-t keypath="list.albums.info" tag="p" scope="global">
|
||||
<template #separator>
|
||||
<br />
|
||||
</template>
|
||||
<template #name>
|
||||
<b v-text="playlistToRemove.name" />
|
||||
</template>
|
||||
</i18n-t>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ControlImage from '@/components/ControlImage.vue'
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialog from '@/components/ModalDialog.vue'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
@@ -77,7 +50,7 @@ import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ListAlbums',
|
||||
components: { ControlImage, ModalDialog, ModalDialogAlbum },
|
||||
components: { ListItem, ModalDialog, ModalDialogAlbum },
|
||||
props: {
|
||||
items: { required: true, type: Object },
|
||||
media_kind: { default: '', type: String }
|
||||
@@ -88,7 +61,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rss_playlist_to_remove: {},
|
||||
playlistToRemove: {},
|
||||
selectedItem: {},
|
||||
showDetailsModal: false,
|
||||
showRemovePodcastModal: false
|
||||
@@ -119,7 +92,7 @@ export default {
|
||||
this.$router.push({ name: 'music-album', params: { id: item.id } })
|
||||
}
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
@@ -128,7 +101,7 @@ export default {
|
||||
.library_album_tracks(this.selectedItem.id, { limit: 1 })
|
||||
.then(({ data: album }) => {
|
||||
webapi.library_track_playlists(album.items[0].id).then(({ data }) => {
|
||||
;[this.rss_playlist_to_remove] = data.items.filter(
|
||||
;[this.playlistToRemove] = data.items.filter(
|
||||
(playlist) => playlist.type === 'rss'
|
||||
)
|
||||
this.showRemovePodcastModal = true
|
||||
@@ -141,11 +114,15 @@ export default {
|
||||
},
|
||||
removePodcast() {
|
||||
this.showRemovePodcastModal = false
|
||||
webapi
|
||||
.library_playlist_delete(this.rss_playlist_to_remove.id)
|
||||
.then(() => {
|
||||
this.$emit('podcast-deleted')
|
||||
})
|
||||
webapi.library_playlist_delete(this.playlistToRemove.id).then(() => {
|
||||
this.$emit('podcast-deleted')
|
||||
})
|
||||
},
|
||||
url(item) {
|
||||
if (this.settingsStore.show_cover_artwork_in_album_lists) {
|
||||
return item.item.artwork_url
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,33 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.id">
|
||||
<div
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item)"
|
||||
>
|
||||
<control-image
|
||||
v-if="settingsStore.show_cover_artwork_in_album_lists"
|
||||
:url="item.images?.[0]?.url ?? ''"
|
||||
:artist="item.artist"
|
||||
:album="item.name"
|
||||
class="media-left is-small"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||
<div
|
||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
||||
v-text="item.artists[0]?.name"
|
||||
/>
|
||||
<div
|
||||
class="is-size-7 has-text-grey"
|
||||
v-text="$filters.toDate(item.release_date)"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-album-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:is-item="item.isItem"
|
||||
:image="url(item)"
|
||||
:index="item.index"
|
||||
:lines="[
|
||||
item.name,
|
||||
item.artists[0]?.name,
|
||||
$filters.toDate(item.release_date)
|
||||
]"
|
||||
@open="open(item)"
|
||||
@open-details="openDetails(item)"
|
||||
/>
|
||||
<modal-dialog-album-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ControlImage from '@/components/ControlImage.vue'
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
|
||||
export default {
|
||||
name: 'ListAlbumsSpotify',
|
||||
components: { ControlImage, ModalDialogAlbumSpotify },
|
||||
components: { ListItem, ModalDialogAlbumSpotify },
|
||||
props: { items: { required: true, type: Object } },
|
||||
setup() {
|
||||
return { settingsStore: useSettingsStore() }
|
||||
@@ -60,9 +42,15 @@ export default {
|
||||
params: { id: item.id }
|
||||
})
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
url(item) {
|
||||
if (this.settingsStore.show_cover_artwork_in_album_lists) {
|
||||
return item.images?.[0]?.url ?? ''
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,31 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div v-if="!item.isItem" class="py-5">
|
||||
<span
|
||||
:id="`index_${item.index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="item.index"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item.item)"
|
||||
>
|
||||
<div class="media-content">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-artist
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.item.name]"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-artist
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListArtists',
|
||||
components: { ModalDialogArtist },
|
||||
components: { ListItem, ModalDialogArtist },
|
||||
props: { items: { required: true, type: Object } },
|
||||
|
||||
data() {
|
||||
return { selectedItem: {}, showDetailsModal: false }
|
||||
},
|
||||
|
||||
methods: {
|
||||
open(item) {
|
||||
this.selectedItem = item
|
||||
@@ -50,7 +33,7 @@ export default {
|
||||
item.media_kind === 'audiobook' ? 'audiobooks-artist' : 'music-artist'
|
||||
this.$router.push({ name: route, params: { id: item.id } })
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.id">
|
||||
<div class="media is-align-items-center is-clickable mb-0">
|
||||
<div class="media-content" @click="open(item)">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-artist-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:is-item="true"
|
||||
:index="item.index"
|
||||
:lines="[item.name]"
|
||||
@open="open(item)"
|
||||
@open-details="openDetails(item)"
|
||||
/>
|
||||
<modal-dialog-artist-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListArtistsSpotify',
|
||||
components: { ModalDialogArtistSpotify },
|
||||
components: { ListItem, ModalDialogArtistSpotify },
|
||||
props: { items: { required: true, type: Object } },
|
||||
|
||||
data() {
|
||||
@@ -38,7 +34,7 @@ export default {
|
||||
params: { id: item.id }
|
||||
})
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
@@ -1,50 +1,31 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div v-if="!item.isItem" class="py-5">
|
||||
<div class="media-content">
|
||||
<span
|
||||
:id="`index_${item.index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="item.index"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item.item)"
|
||||
>
|
||||
<div class="media-content">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-composer
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.item.name]"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-composer
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListComposers',
|
||||
components: { ModalDialogComposer },
|
||||
components: { ListItem, ModalDialogComposer },
|
||||
props: { items: { required: true, type: Object } },
|
||||
|
||||
data() {
|
||||
return { selectedItem: {}, showDetailsModal: false }
|
||||
},
|
||||
|
||||
methods: {
|
||||
open(item) {
|
||||
this.selectedItem = item
|
||||
@@ -53,8 +34,7 @@ export default {
|
||||
params: { name: item.name }
|
||||
})
|
||||
},
|
||||
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
@@ -27,22 +27,20 @@
|
||||
>
|
||||
<mdicon class="media-left icon" name="folder" />
|
||||
<div class="media-content">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item)">
|
||||
<a @click.prevent.stop="openDetails(item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-directory
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<modal-dialog-directory
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -80,7 +78,7 @@ export default {
|
||||
}
|
||||
this.$router.push(route)
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item.path
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
|
||||
@@ -1,45 +1,28 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div v-if="!item.isItem" class="py-5">
|
||||
<div class="media-content">
|
||||
<span
|
||||
:id="`index_${item.index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="item.index"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item.item)"
|
||||
>
|
||||
<div class="media-content">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-genre
|
||||
:item="selectedItem"
|
||||
:media_kind="media_kind"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.item.name]"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-genre
|
||||
:item="selectedItem"
|
||||
:media_kind="media_kind"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListGenres',
|
||||
components: { ModalDialogGenre },
|
||||
components: { ListItem, ModalDialogGenre },
|
||||
props: {
|
||||
items: { required: true, type: Object },
|
||||
media_kind: { required: true, type: String }
|
||||
@@ -55,7 +38,7 @@ export default {
|
||||
query: { media_kind: this.media_kind }
|
||||
})
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
80
web-src/src/components/ListItem.vue
Normal file
80
web-src/src/components/ListItem.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div v-if="!isItem" class="py-5">
|
||||
<div class="media-content">
|
||||
<span
|
||||
:id="`index_${index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="index"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
:class="{ 'with-progress': progress }"
|
||||
@click="open"
|
||||
>
|
||||
<mdicon v-if="icon" class="media-left icon" :name="icon" />
|
||||
<control-image v-if="image" :url="image" class="media-left is-small" />
|
||||
<div class="media-content">
|
||||
<div
|
||||
v-for="(line, position) in lines"
|
||||
:key="position"
|
||||
:class="{
|
||||
'is-size-6': position === 0,
|
||||
'is-size-7': position !== 0,
|
||||
'has-text-weight-bold': position !== 2,
|
||||
'has-text-grey': position !== 0
|
||||
}"
|
||||
v-text="line"
|
||||
/>
|
||||
<progress
|
||||
v-if="progress"
|
||||
class="progress is-dark"
|
||||
max="1"
|
||||
:value="progress"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDetails">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ControlImage from '@/components/ControlImage.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListItem',
|
||||
components: { ControlImage },
|
||||
props: {
|
||||
icon: { default: null, type: String },
|
||||
image: { default: null, type: String },
|
||||
index: { default: null, type: [String, Number] },
|
||||
isItem: { default: true, type: Boolean },
|
||||
lines: { default: null, type: Array },
|
||||
progress: { default: null, type: Number }
|
||||
},
|
||||
emits: ['open', 'openDetails'],
|
||||
methods: {
|
||||
open() {
|
||||
this.$emit('open')
|
||||
},
|
||||
openDetails() {
|
||||
this.$emit('openDetails')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.progress {
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.media.with-progress {
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
</style>
|
||||
@@ -52,7 +52,7 @@ import webapi from '@/webapi'
|
||||
export default {
|
||||
name: 'ListItemQueueItem',
|
||||
props: {
|
||||
current_position: { required: true, type: Number },
|
||||
currentPosition: { required: true, type: Number },
|
||||
editing: Boolean,
|
||||
item: { required: true, type: Object },
|
||||
position: { required: true, type: Number },
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
return this.item.id === this.playerStore.item_id
|
||||
},
|
||||
isNext() {
|
||||
return this.current_position < 0 || this.position >= this.current_position
|
||||
return this.currentPosition < 0 || this.position >= this.currentPosition
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item.item)"
|
||||
>
|
||||
<mdicon class="media-left icon" :name="icon(item.item)" />
|
||||
<div class="media-content">
|
||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-playlist
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:icon="icon(item.item)"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.item.name]"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-playlist
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListPlaylists',
|
||||
components: { ModalDialogPlaylist },
|
||||
components: { ListItem, ModalDialogPlaylist },
|
||||
props: { items: { required: true, type: Object } },
|
||||
|
||||
data() {
|
||||
return { selectedItem: {}, showDetailsModal: false }
|
||||
},
|
||||
|
||||
methods: {
|
||||
icon(item) {
|
||||
if (item.type === 'folder') {
|
||||
@@ -52,7 +43,7 @@ export default {
|
||||
this.$router.push({ name: 'playlist', params: { id: item.id } })
|
||||
}
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
@@ -1,40 +1,27 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.id">
|
||||
<div
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
@click="open(item)"
|
||||
>
|
||||
<div class="media-content">
|
||||
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||
<div
|
||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
||||
v-text="item.owner.display_name"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-playlist-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.name, item.owner.display_name]"
|
||||
@open="open(item)"
|
||||
@open-details="openDetails(item)"
|
||||
/>
|
||||
<modal-dialog-playlist-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
|
||||
|
||||
export default {
|
||||
name: 'ListPlaylistsSpotify',
|
||||
components: {
|
||||
ModalDialogPlaylistSpotify
|
||||
},
|
||||
components: { ListItem, ModalDialogPlaylistSpotify },
|
||||
props: { items: { required: true, type: Object } },
|
||||
|
||||
data() {
|
||||
@@ -45,7 +32,7 @@ export default {
|
||||
open(item) {
|
||||
this.$router.push({ name: 'playlist-spotify', params: { id: item.id } })
|
||||
},
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
}
|
||||
|
||||
@@ -1,73 +1,36 @@
|
||||
<template>
|
||||
<template v-for="item in items" :key="item.itemId">
|
||||
<div v-if="!item.isItem" class="py-5">
|
||||
<span
|
||||
:id="`index_${item.index}`"
|
||||
class="tag is-small has-text-weight-bold"
|
||||
v-text="item.index"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="media is-align-items-center is-clickable mb-0"
|
||||
:class="{ 'with-progress': showProgress }"
|
||||
@click="play(item.item)"
|
||||
>
|
||||
<mdicon
|
||||
v-if="showIcon"
|
||||
class="media-left icon"
|
||||
name="file-music-outline"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div
|
||||
class="is-size-6 has-text-weight-bold"
|
||||
:class="{
|
||||
'has-text-grey':
|
||||
item.item.media_kind === 'podcast' && item.item.play_count > 0
|
||||
}"
|
||||
v-text="item.item.title"
|
||||
/>
|
||||
<div
|
||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
||||
v-text="item.item.artist"
|
||||
/>
|
||||
<div class="is-size-7 has-text-grey" v-text="item.item.album" />
|
||||
<progress
|
||||
v-if="showProgress && item.item.seek_ms > 0"
|
||||
class="progress is-dark"
|
||||
:max="item.item.length_ms"
|
||||
:value="item.item.seek_ms"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item.item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-track
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
@play-count-changed="$emit('play-count-changed')"
|
||||
/>
|
||||
</teleport>
|
||||
<list-item
|
||||
v-for="item in items"
|
||||
:key="item.itemId"
|
||||
:icon="icon"
|
||||
:is-item="item.isItem"
|
||||
:index="item.index"
|
||||
:lines="[item.item.title, item.item.artist, item.item.album]"
|
||||
:progress="progress(item.item)"
|
||||
@open="open(item.item)"
|
||||
@open-details="openDetails(item.item)"
|
||||
/>
|
||||
<modal-dialog-track
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
@play-count-changed="$emit('play-count-changed')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItem from '@/components/ListItem.vue'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ListTracks',
|
||||
components: { ModalDialogTrack },
|
||||
components: { ListItem, ModalDialogTrack },
|
||||
props: {
|
||||
expression: { default: '', type: String },
|
||||
items: { required: true, type: Object },
|
||||
showIcon: Boolean,
|
||||
showProgress: Boolean,
|
||||
items: { default: null, type: Object },
|
||||
icon: { default: null, type: String },
|
||||
showProgress: { default: false, type: Boolean },
|
||||
uris: { default: '', type: String }
|
||||
},
|
||||
emits: ['play-count-changed'],
|
||||
@@ -75,11 +38,7 @@ export default {
|
||||
return { selectedItem: {}, showDetailsModal: false }
|
||||
},
|
||||
methods: {
|
||||
openDialog(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
play(item) {
|
||||
open(item) {
|
||||
if (this.uris) {
|
||||
webapi.player_play_uri(this.uris, false, this.items.items.indexOf(item))
|
||||
} else if (this.expression) {
|
||||
@@ -91,17 +50,20 @@ export default {
|
||||
} else {
|
||||
webapi.player_play_uri(item.uri, false)
|
||||
}
|
||||
},
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
// 'has-text-grey': item.item.media_kind === 'podcast' && item.item.play_count > 0
|
||||
progress(item) {
|
||||
if (item.item) {
|
||||
if (this.showProgress && item.item.seek_ms > 0) {
|
||||
return item.item.seek_ms / item.item.length_ms
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.progress {
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.media.with-progress {
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,19 +36,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click.prevent.stop="openDialog(item)">
|
||||
<a @click.prevent.stop="openDetails(item)">
|
||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<teleport to="#app">
|
||||
<modal-dialog-track-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</teleport>
|
||||
<modal-dialog-track-spotify
|
||||
:item="selectedItem"
|
||||
:show="showDetailsModal"
|
||||
@close="showDetailsModal = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -66,7 +64,7 @@ export default {
|
||||
return { selectedItem: {}, showDetailsModal: false }
|
||||
},
|
||||
methods: {
|
||||
openDialog(item) {
|
||||
openDetails(item) {
|
||||
this.selectedItem = item
|
||||
this.showDetailsModal = true
|
||||
},
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
class="lyrics"
|
||||
@touchstart="autoScrolling = false"
|
||||
@touchend="autoScrolling = true"
|
||||
@scroll.passive="start_scrolling"
|
||||
@wheel.passive="start_scrolling"
|
||||
@scroll.passive="startScrolling"
|
||||
@wheel.passive="startScrolling"
|
||||
>
|
||||
<template v-for="(verse, index) in lyrics" :key="index">
|
||||
<div
|
||||
v-if="index === verse_index"
|
||||
v-if="index === verseIndex"
|
||||
:class="{ 'is-highlighted': playerStore.isPlaying }"
|
||||
>
|
||||
<span
|
||||
@@ -85,7 +85,7 @@ export default {
|
||||
}
|
||||
return parsed
|
||||
},
|
||||
verse_index() {
|
||||
verseIndex() {
|
||||
if (this.lyrics.length && this.lyrics[0].time) {
|
||||
const currentTime = this.playerStore.item_progress_ms / 1000,
|
||||
la = this.lyrics,
|
||||
@@ -96,7 +96,7 @@ export default {
|
||||
la[this.lastIndex].time > currentTime
|
||||
// Reset the cache when the track has changed or has been seeked
|
||||
if (trackChanged || trackSeeked) {
|
||||
this.reset_scrolling()
|
||||
this.resetScrolling()
|
||||
}
|
||||
// Check the next two items and the last one before searching
|
||||
if (
|
||||
@@ -134,20 +134,20 @@ export default {
|
||||
}
|
||||
return index
|
||||
}
|
||||
this.reset_scrolling()
|
||||
this.resetScrolling()
|
||||
return -1
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
verse_index() {
|
||||
verseIndex() {
|
||||
if (this.autoScrolling) {
|
||||
this.scroll_to_verse()
|
||||
this.scrollToVerse()
|
||||
}
|
||||
this.lastIndex = this.verse_index
|
||||
this.lastIndex = this.verseIndex
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset_scrolling() {
|
||||
resetScrolling() {
|
||||
// Scroll to the start of the lyrics in all cases
|
||||
if (this.playerStore.item_id !== this.lastItemId && this.$refs.lyrics) {
|
||||
this.$refs.lyrics.scrollTo(0, 0)
|
||||
@@ -155,13 +155,13 @@ export default {
|
||||
this.lastItemId = this.playerStore.item_id
|
||||
this.lastIndex = -1
|
||||
},
|
||||
scroll_to_verse() {
|
||||
scrollToVerse() {
|
||||
const pane = this.$refs.lyrics
|
||||
if (this.verse_index === -1) {
|
||||
if (this.verseIndex === -1) {
|
||||
pane.scrollTo(0, 0)
|
||||
return
|
||||
}
|
||||
const currentVerse = pane.children[this.verse_index]
|
||||
const currentVerse = pane.children[this.verseIndex]
|
||||
pane.scrollBy({
|
||||
behavior: 'smooth',
|
||||
left: 0,
|
||||
@@ -172,7 +172,7 @@ export default {
|
||||
pane.scrollTop
|
||||
})
|
||||
},
|
||||
start_scrolling(event) {
|
||||
startScrolling(event) {
|
||||
// Consider only user events
|
||||
if (event.screenX ?? event.screenY) {
|
||||
this.autoScrolling = false
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
<p class="control has-icons-left">
|
||||
<input
|
||||
ref="playlist_name_field"
|
||||
v-model="playlist_name"
|
||||
v-model="playlistName"
|
||||
class="input"
|
||||
type="text"
|
||||
pattern=".+"
|
||||
required
|
||||
:placeholder="$t('dialog.playlist.save.playlist-name')"
|
||||
:disabled="loading"
|
||||
@input="check_name"
|
||||
@input="check"
|
||||
/>
|
||||
<mdicon class="icon is-left" name="playlist-music" size="16" />
|
||||
</p>
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
return {
|
||||
disabled: true,
|
||||
loading: false,
|
||||
playlist_name: ''
|
||||
playlistName: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -75,17 +75,17 @@ export default {
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
check_name(event) {
|
||||
check(event) {
|
||||
const { validity } = event.target
|
||||
this.disabled = validity.patternMismatch || validity.valueMissing
|
||||
},
|
||||
save() {
|
||||
this.loading = true
|
||||
webapi
|
||||
.queue_save_playlist(this.playlist_name)
|
||||
.queue_save_playlist(this.playlistName)
|
||||
.then(() => {
|
||||
this.$emit('close')
|
||||
this.playlist_name = ''
|
||||
this.playlistName = ''
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
spotify_track: {}
|
||||
spotifyTrack: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -89,10 +89,10 @@ export default {
|
||||
spotifyApi
|
||||
.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1))
|
||||
.then((response) => {
|
||||
this.spotify_track = response
|
||||
this.spotifyTrack = response
|
||||
})
|
||||
} else {
|
||||
this.spotify_track = {}
|
||||
this.spotifyTrack = {}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -102,7 +102,7 @@ export default {
|
||||
if (this.item.data_kind === 'spotify') {
|
||||
this.$router.push({
|
||||
name: 'music-spotify-album',
|
||||
params: { id: this.spotify_track.album.id }
|
||||
params: { id: this.spotifyTrack.album.id }
|
||||
})
|
||||
} else if (this.item.media_kind === 'podcast') {
|
||||
this.$router.push({
|
||||
@@ -126,7 +126,7 @@ export default {
|
||||
if (this.item.data_kind === 'spotify') {
|
||||
this.$router.push({
|
||||
name: 'music-spotify-artist',
|
||||
params: { id: this.spotify_track.artists[0].id }
|
||||
params: { id: this.spotifyTrack.artists[0].id }
|
||||
})
|
||||
} else if (
|
||||
this.item.media_kind === 'music' ||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<control-switch v-model="rescan_metadata">
|
||||
<control-switch v-model="rescanMetadata">
|
||||
<template #label>
|
||||
<span v-text="$t('dialog.update.rescan-metadata')" />
|
||||
</template>
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rescan_metadata: false
|
||||
rescanMetadata: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
analyse() {
|
||||
if (this.rescan_metadata) {
|
||||
if (this.rescanMetadata) {
|
||||
webapi.library_rescan(this.libraryStore.update_dialog_scan_kind)
|
||||
} else {
|
||||
webapi.library_update(this.libraryStore.update_dialog_scan_kind)
|
||||
|
||||
@@ -107,6 +107,12 @@ export default {
|
||||
show: true,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
key: 'navigation.composers',
|
||||
name: 'music-composers',
|
||||
show: true,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
key: 'navigation.spotify',
|
||||
name: 'music-spotify',
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<template>
|
||||
<section v-if="notifications.length > 0" class="notifications">
|
||||
<section v-if="!notificationsStore.isEmpty" class="notifications">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-half">
|
||||
<div
|
||||
v-for="notification in notifications"
|
||||
v-for="notification in notificationsStore.list"
|
||||
:key="notification.id"
|
||||
class="notification"
|
||||
:class="notification.type ? `is-${notification.type}` : ''"
|
||||
>
|
||||
<button class="delete" @click="remove(notification)" />
|
||||
<button
|
||||
class="delete"
|
||||
@click="notificationsStore.remove(notification)"
|
||||
/>
|
||||
<div class="text" v-text="notification.text" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,21 +24,8 @@ import { useNotificationsStore } from '@/stores/notifications'
|
||||
|
||||
export default {
|
||||
name: 'NotificationList',
|
||||
|
||||
setup() {
|
||||
return { notificationsStore: useNotificationsStore() }
|
||||
},
|
||||
|
||||
computed: {
|
||||
notifications() {
|
||||
return this.notificationsStore.list
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
remove(notification) {
|
||||
this.notificationsStore.remove(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user