mirror of
https://github.com/koel/koel
synced 2024-11-24 21:23:06 +00:00
migration: song list controls
This commit is contained in:
parent
6a06e5ef9b
commit
c3880df2bc
37 changed files with 201 additions and 215 deletions
|
@ -1,6 +1,6 @@
|
||||||
import Component from '@/components/screens/album.vue'
|
import Component from '@/components/screens/album.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import { download, albumInfo as albumInfoService, playback } from '@/services'
|
import { albumInfo as albumInfoService, download } from '@/services'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { mock } from '@/__tests__/__helpers__'
|
import { mock } from '@/__tests__/__helpers__'
|
||||||
import { mount, shallow } from '@/__tests__/adapter'
|
import { mount, shallow } from '@/__tests__/adapter'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Component from '@/components/screens/all-songs.vue'
|
import Component from '@/components/screens/AllSongsScreen.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { songStore } from '@/stores'
|
import { songStore } from '@/stores'
|
||||||
import { mount } from '@/__tests__/adapter'
|
import { mount } from '@/__tests__/adapter'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Component from '@/components/screens/artist.vue'
|
import Component from '@/components/screens/artist.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import { download, artistInfo as artistInfoService, playback } from '@/services'
|
import { artistInfo as artistInfoService, download } from '@/services'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { mock } from '@/__tests__/__helpers__'
|
import { mock } from '@/__tests__/__helpers__'
|
||||||
import { mount, shallow } from '@/__tests__/adapter'
|
import { mount, shallow } from '@/__tests__/adapter'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Component from '@/components/screens/favorites.vue'
|
import Component from '@/components/screens/favorites.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import SongListControls from '@/components/song/list-controls.vue'
|
import SongListControls from '@/components/songSongListControls.vue'
|
||||||
import { download } from '@/services'
|
import { download } from '@/services'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { mock } from '@/__tests__/__helpers__'
|
import { mock } from '@/__tests__/__helpers__'
|
||||||
|
@ -45,7 +45,7 @@ describe('components/screens/favorites', () => {
|
||||||
shallow(Component, {
|
shallow(Component, {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
state: {
|
state: {
|
||||||
songs: factory('song', 5),
|
songs: factory('song', 5)
|
||||||
},
|
},
|
||||||
sharedState: { allowDownload: true },
|
sharedState: { allowDownload: true },
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Component from '@/components/screens/playlist.vue'
|
import Component from '@/components/screens/playlist.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
import { playlistStore } from '@/stores'
|
import { playlistStore } from '@/stores'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Component from '@/components/screens/queue.vue'
|
import Component from '@/components/screens/queue.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { queueStore, songStore } from '@/stores'
|
import { queueStore, songStore } from '@/stores'
|
||||||
import { playback } from '@/services'
|
import { playback } from '@/services'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Component from '@/components/screens/recently-played.vue'
|
import Component from '@/components/screens/recently-played.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { recentlyPlayedStore } from '@/stores'
|
import { recentlyPlayedStore } from '@/stores'
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Component from '@/components/song/list-controls.vue'
|
import Component from '@/components/songSongListControls.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { take } from 'lodash'
|
import { take } from 'lodash'
|
||||||
import { shallow, mount } from '@/__tests__/adapter'
|
import { shallow, mount } from '@/__tests__/adapter'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import Component from '@/components/song/list.vue'
|
import Component from '@/components/song/SongList.vue'
|
||||||
import factory from '@/__tests__/factory'
|
import factory from '@/__tests__/factory'
|
||||||
import { queueStore } from '@/stores'
|
import { queueStore } from '@/stores'
|
||||||
import { playback } from '@/services'
|
import { playback } from '@/services'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Component from '@/components/ui/screen-controls-toggler.vue'
|
import Component from '@/components/ui/ScreenControlsToggler.vue'
|
||||||
import isMobile from 'ismobilejs'
|
import isMobile from 'ismobilejs'
|
||||||
import { shallow } from '@/__tests__/adapter'
|
import { shallow } from '@/__tests__/adapter'
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ import HomeScreen from '@/components/screens/home.vue'
|
||||||
import QueueScreen from '@/components/screens/queue.vue'
|
import QueueScreen from '@/components/screens/queue.vue'
|
||||||
import AlbumListScreen from '@/components/screens/album-list.vue'
|
import AlbumListScreen from '@/components/screens/album-list.vue'
|
||||||
import ArtistListScreen from '@/components/screens/artist-list.vue'
|
import ArtistListScreen from '@/components/screens/artist-list.vue'
|
||||||
import AllSongsScreen from '@/components/screens/all-songs.vue'
|
import AllSongsScreen from '@/components/screens/AllSongsScreen.vue'
|
||||||
import PlaylistScreen from '@/components/screens/playlist.vue'
|
import PlaylistScreen from '@/components/screens/playlist.vue'
|
||||||
import FavoritesScreen from '@/components/screens/favorites.vue'
|
import FavoritesScreen from '@/components/screens/favorites.vue'
|
||||||
|
|
||||||
|
|
|
@ -10,30 +10,33 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="state.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
@playAll="playAll"
|
@playAll="playAll"
|
||||||
@playSelected="playSelected"
|
@playSelected="playSelected"
|
||||||
:songs="state.songs"
|
:songs="songs"
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList :items="state.songs" type="all-songs" ref="songList"/>
|
<SongList :items="songs" type="all-songs" ref="songList"/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { defineAsyncComponent, toRef } from 'vue'
|
||||||
import { pluralize } from '@/utils'
|
import { pluralize } from '@/utils'
|
||||||
import { songStore } from '@/stores'
|
import { songStore } from '@/stores'
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { defineAsyncComponent, reactive } from 'vue'
|
|
||||||
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
|
@ -43,8 +46,5 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(toRef(songStore.state, 'songs'))
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
|
||||||
const state = reactive(songStore.state)
|
|
||||||
</script>
|
</script>
|
|
@ -20,7 +20,7 @@ import { eventBus, limitBy } from '@/utils'
|
||||||
import { albumStore, preferenceStore as preferences } from '@/stores'
|
import { albumStore, preferenceStore as preferences } from '@/stores'
|
||||||
import { useInfiniteScroll } from '@/composables'
|
import { useInfiniteScroll } from '@/composables'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const AlbumCard = defineAsyncComponent(() => import('@/components/album/card.vue'))
|
const AlbumCard = defineAsyncComponent(() => import('@/components/album/card.vue'))
|
||||||
const ViewModeSwitch = defineAsyncComponent(() => import('@/components/ui/view-mode-switch.vue'))
|
const ViewModeSwitch = defineAsyncComponent(() => import('@/components/ui/view-mode-switch.vue'))
|
||||||
|
|
||||||
|
|
|
@ -9,22 +9,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:meta>
|
<template v-slot:meta>
|
||||||
<span v-if="album.songs.length">
|
<span v-if="songs.length">
|
||||||
by
|
by
|
||||||
<a class="artist" v-if="isNormalArtist" :href="`#!/artist/${album.artist.id}`">{{ album.artist.name }}</a>
|
<a v-if="isNormalArtist" :href="`#!/artist/${album.artist.id}`" class="artist">{{ album.artist.name }}</a>
|
||||||
<span class="nope" v-else>{{ album.artist.name }}</span>
|
<span class="nope" v-else>{{ album.artist.name }}</span>
|
||||||
•
|
•
|
||||||
{{ pluralize(album.songs.length, 'song') }}
|
{{ pluralize(songs.length, 'song') }}
|
||||||
•
|
•
|
||||||
{{ fmtLength }}
|
{{ fmtLength }}
|
||||||
|
|
||||||
<template v-if="sharedState.useLastfm">
|
<template v-if="sharedState.useLastfm">
|
||||||
•
|
•
|
||||||
<a class="info" href @click.prevent="showInfo" title="View album's extra information">Info</a>
|
<a class="info" href title="View album's extra information" @click.prevent="showInfo">Info</a>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="sharedState.allowDownload">
|
<template v-if="sharedState.allowDownload">
|
||||||
•
|
•
|
||||||
<a class="download" href @click.prevent="download" title="Download all songs in album" role="button">
|
<a class="download" href role="button" title="Download all songs in album" @click.prevent="download">
|
||||||
Download All
|
Download All
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
@ -33,19 +33,19 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="album.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
@playAll="playAll"
|
|
||||||
@playSelected="playSelected"
|
|
||||||
:songs="album.songs"
|
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
|
:songs="songs"
|
||||||
|
@playAll="playAll"
|
||||||
|
@playSelected="playSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList :items="album.songs" type="album" :config="listConfig" ref="songList"/>
|
<SongList :items="songs" type="album" :config="listConfig" ref="songList"/>
|
||||||
|
|
||||||
<section class="info-wrapper" v-if="sharedState.useLastfm && showing">
|
<section v-if="sharedState.useLastfm && showing" class="info-wrapper">
|
||||||
<CloseModalBtn @click="showing = false"/>
|
<CloseModalBtn @click="showing = false"/>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="loading" v-if="loading">
|
<div class="loading" v-if="loading">
|
||||||
|
@ -65,16 +65,20 @@ import { albumInfo as albumInfoService, download as downloadService } from '@/se
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { useAlbumAttributes, useSongList } from '@/composables'
|
import { useAlbumAttributes, useSongList } from '@/composables'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const AlbumInfo = defineAsyncComponent(() => import('@/components/album/AlbumInfo.vue'))
|
const AlbumInfo = defineAsyncComponent(() => import('@/components/album/AlbumInfo.vue'))
|
||||||
const SoundBar = defineAsyncComponent(() => import('@/components/ui/sound-bar.vue'))
|
const SoundBar = defineAsyncComponent(() => import('@/components/ui/sound-bar.vue'))
|
||||||
const AlbumThumbnail = defineAsyncComponent(() => import('@/components/ui/AlbumArtistThumbnail.vue'))
|
const AlbumThumbnail = defineAsyncComponent(() => import('@/components/ui/AlbumArtistThumbnail.vue'))
|
||||||
const CloseModalBtn = defineAsyncComponent(() => import('@/components/ui/close-modal-btn.vue'))
|
const CloseModalBtn = defineAsyncComponent(() => import('@/components/ui/close-modal-btn.vue'))
|
||||||
|
|
||||||
|
const props = defineProps<{ album: Album }>()
|
||||||
|
const { album } = toRefs(props)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
showingControls,
|
showingControls,
|
||||||
|
@ -83,10 +87,7 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(ref(album.value.songs))
|
||||||
|
|
||||||
const props = defineProps<{ album: Album }>()
|
|
||||||
const { album } = toRefs(props)
|
|
||||||
|
|
||||||
const { length, fmtLength } = useAlbumAttributes(album.value)
|
const { length, fmtLength } = useAlbumAttributes(album.value)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ const {
|
||||||
makeScrollable
|
makeScrollable
|
||||||
} = useInfiniteScroll(9)
|
} = useInfiniteScroll(9)
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
||||||
const ViewModeSwitch = defineAsyncComponent(() => import('@/components/ui/view-mode-switch.vue'))
|
const ViewModeSwitch = defineAsyncComponent(() => import('@/components/ui/view-mode-switch.vue'))
|
||||||
|
|
||||||
|
|
|
@ -9,26 +9,26 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:meta>
|
<template v-slot:meta>
|
||||||
<span v-if="artist.songs.length">
|
<span v-if="songs.length">
|
||||||
{{ pluralize(artist.albums.length, 'album') }}
|
{{ pluralize(artist.albums.length, 'album') }}
|
||||||
•
|
•
|
||||||
{{ pluralize(artist.songs.length, 'song') }}
|
{{ pluralize(songs.length, 'song') }}
|
||||||
•
|
•
|
||||||
{{ fmtLength }}
|
{{ fmtLength }}
|
||||||
|
|
||||||
<template v-if="sharedState.useLastfm">
|
<template v-if="sharedState.useLastfm">
|
||||||
•
|
•
|
||||||
<a class="info" href @click.prevent="showInfo" title="View artist's extra information">Info</a>
|
<a class="info" href title="View artist's extra information" @click.prevent="showInfo">Info</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="sharedState.allowDownload">
|
<template v-if="sharedState.allowDownload">
|
||||||
•
|
•
|
||||||
<a
|
<a
|
||||||
@click.prevent="download"
|
|
||||||
class="download"
|
class="download"
|
||||||
href
|
href
|
||||||
role="button"
|
role="button"
|
||||||
title="Download all songs by this artist"
|
title="Download all songs by this artist"
|
||||||
|
@click.prevent="download"
|
||||||
>
|
>
|
||||||
Download All
|
Download All
|
||||||
</a>
|
</a>
|
||||||
|
@ -38,17 +38,17 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="artist.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
@playAll="playAll"
|
|
||||||
@playSelected="playSelected"
|
|
||||||
:songs="artist.songs"
|
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
|
:songs="songs"
|
||||||
|
@playAll="playAll"
|
||||||
|
@playSelected="playSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList :items="artist.songs" type="artist" :config="listConfig" ref="songList"/>
|
<SongList :items="songs" type="artist" :config="listConfig" ref="songList"/>
|
||||||
|
|
||||||
<section class="info-wrapper" v-if="sharedState.useLastfm && showing">
|
<section class="info-wrapper" v-if="sharedState.useLastfm && showing">
|
||||||
<CloseModalBtn @click="showing = false"/>
|
<CloseModalBtn @click="showing = false"/>
|
||||||
|
@ -70,12 +70,15 @@ import { artistInfo as artistInfoService, download as downloadService } from '@/
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { useArtistAttributes, useSongList } from '@/composables'
|
import { useArtistAttributes, useSongList } from '@/composables'
|
||||||
|
|
||||||
|
const props = defineProps<{ artist: Artist }>()
|
||||||
|
const { artist } = toRefs(props)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
songList,
|
songList,
|
||||||
state,
|
songs,
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
showingControls,
|
showingControls,
|
||||||
|
@ -84,13 +87,11 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(ref(artist.value.songs))
|
||||||
|
|
||||||
const props = defineProps<{ artist: Artist }>()
|
|
||||||
const { artist } = toRefs(props)
|
|
||||||
const { length, fmtLength, image } = useArtistAttributes(artist.value)
|
const { length, fmtLength, image } = useArtistAttributes(artist.value)
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ArtistInfo = defineAsyncComponent(() => import('@/components/artist/info.vue'))
|
const ArtistInfo = defineAsyncComponent(() => import('@/components/artist/info.vue'))
|
||||||
const SoundBar = defineAsyncComponent(() => import('@/components/ui/sound-bar.vue'))
|
const SoundBar = defineAsyncComponent(() => import('@/components/ui/sound-bar.vue'))
|
||||||
const ArtistThumbnail = defineAsyncComponent(() => import('@/components/ui/AlbumArtistThumbnail.vue'))
|
const ArtistThumbnail = defineAsyncComponent(() => import('@/components/ui/AlbumArtistThumbnail.vue'))
|
||||||
|
@ -112,7 +113,6 @@ watch(() => artist.value.albums.length, newAlbumCount => newAlbumCount || router
|
||||||
|
|
||||||
watch(artist, () => {
|
watch(artist, () => {
|
||||||
showing.value = false
|
showing.value = false
|
||||||
// @ts-ignore
|
|
||||||
songList.value?.sort()
|
songList.value?.sort()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
{{ pluralize(meta.songCount, 'song') }}
|
{{ pluralize(meta.songCount, 'song') }}
|
||||||
•
|
•
|
||||||
{{ meta.totalLength }}
|
{{ meta.totalLength }}
|
||||||
<template v-if="sharedState.allowDownload && state.songs.length">
|
<template v-if="allowDownload && songs.length">
|
||||||
•
|
•
|
||||||
<a href @click.prevent="download" class="download" title="Download all songs in playlist" role="button">
|
<a class="download" href role="button" title="Download all songs in playlist" @click.prevent="download">
|
||||||
Download All
|
Download All
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
@ -20,17 +20,17 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="state.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
@playAll="playAll"
|
|
||||||
@playSelected="playSelected"
|
|
||||||
:songs="state.songs"
|
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
|
:songs="songs"
|
||||||
|
@playAll="playAll"
|
||||||
|
@playSelected="playSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList v-if="state.songs.length" :items="state.songs" type="favorites" ref="songList"/>
|
<SongList v-if="songs.length" ref="songList" :items="songs" type="favorites"/>
|
||||||
|
|
||||||
<ScreenPlaceholder v-else>
|
<ScreenPlaceholder v-else>
|
||||||
<template v-slot:icon>
|
<template v-slot:icon>
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
</template>
|
</template>
|
||||||
No favorites yet.
|
No favorites yet.
|
||||||
<span class="secondary d-block">
|
<span class="secondary d-block">
|
||||||
Click the
|
Click the
|
||||||
<i class="fa fa-heart-o"></i>
|
<i class="fa fa-heart-o"></i>
|
||||||
icon to mark a song as favorite.
|
icon to mark a song as favorite.
|
||||||
</span>
|
</span>
|
||||||
</ScreenPlaceholder>
|
</ScreenPlaceholder>
|
||||||
|
@ -51,15 +51,16 @@ import { pluralize } from '@/utils'
|
||||||
import { favoriteStore, sharedStore } from '@/stores'
|
import { favoriteStore, sharedStore } from '@/stores'
|
||||||
import { download as downloadService } from '@/services'
|
import { download as downloadService } from '@/services'
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { defineAsyncComponent, reactive } from 'vue'
|
import { defineAsyncComponent, toRef } from 'vue'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
|
@ -69,10 +70,9 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(toRef(favoriteStore.state, 'songs'))
|
||||||
|
|
||||||
const state = reactive(favoriteStore.state)
|
const allowDownload = toRef(sharedStore.state, 'allowDownload')
|
||||||
const sharedState = reactive(sharedStore.state)
|
|
||||||
|
|
||||||
const download = () => downloadService.fromFavorites()
|
const download = () => downloadService.fromFavorites()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -89,7 +89,7 @@ import router from '@/router'
|
||||||
import { useInfiniteScroll } from '@/composables'
|
import { useInfiniteScroll } from '@/composables'
|
||||||
import { computed, defineAsyncComponent, reactive, ref } from 'vue'
|
import { computed, defineAsyncComponent, reactive, ref } from 'vue'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const AlbumCard = defineAsyncComponent(() => import('@/components/album/card.vue'))
|
const AlbumCard = defineAsyncComponent(() => import('@/components/album/card.vue'))
|
||||||
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
||||||
const SongCard = defineAsyncComponent(() => import('@/components/song/card.vue'))
|
const SongCard = defineAsyncComponent(() => import('@/components/song/card.vue'))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<section id="playlistWrapper">
|
<section id="playlistWrapper" v-if="playlist">
|
||||||
<ScreenHeader>
|
<ScreenHeader>
|
||||||
{{ playlist.name }}
|
{{ playlist.name }}
|
||||||
<ControlsToggler v-if="playlist.populated" :showing-controls="showingControls" @toggleControls="toggleControls"/>
|
<ControlsToggler v-if="playlist.populated" :showing-controls="showingControls" @toggleControls="toggleControls"/>
|
||||||
|
@ -33,11 +33,11 @@
|
||||||
|
|
||||||
<template v-if="playlist.populated">
|
<template v-if="playlist.populated">
|
||||||
<SongList
|
<SongList
|
||||||
v-if="playlist.songs.length"
|
v-if="songs.length"
|
||||||
:items="playlist.songs"
|
ref="songList"
|
||||||
|
:items="songs"
|
||||||
:playlist="playlist"
|
:playlist="playlist"
|
||||||
type="playlist"
|
type="playlist"
|
||||||
ref="songList"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScreenPlaceholder v-else>
|
<ScreenPlaceholder v-else>
|
||||||
|
@ -62,23 +62,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { eventBus } from '@/utils'
|
import { defineAsyncComponent, nextTick, ref } from 'vue'
|
||||||
|
import { eventBus, pluralize } from '@/utils'
|
||||||
import { playlistStore, sharedStore } from '@/stores'
|
import { playlistStore, sharedStore } from '@/stores'
|
||||||
import { download as downloadService } from '@/services'
|
import { download as downloadService } from '@/services'
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { defineAsyncComponent, nextTick, reactive, ref } from 'vue'
|
|
||||||
import { pluralize } from '@/utils'
|
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
|
const playlist = ref<Playlist>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
meta,
|
meta,
|
||||||
state,
|
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
showingControls,
|
showingControls,
|
||||||
songListControlConfig,
|
songListControlConfig,
|
||||||
|
@ -86,15 +87,12 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList({
|
} = useSongList(ref(playlist.value?.songs || []), { deletePlaylist: true })
|
||||||
deletePlaylist: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const playlist = ref<Playlist>(playlistStore.stub)
|
const sharedState = sharedStore.state
|
||||||
const sharedState = reactive(sharedStore.state)
|
|
||||||
|
|
||||||
const destroy = () => eventBus.emit('PLAYLIST_DELETE', playlist.value)
|
const destroy = () => eventBus.emit('PLAYLIST_DELETE', playlist.value)
|
||||||
const download = () => downloadService.fromPlaylist(playlist.value)
|
const download = () => downloadService.fromPlaylist(playlist.value!)
|
||||||
const editSmartPlaylist = () => eventBus.emit('MODAL_SHOW_EDIT_SMART_PLAYLIST_FORM', playlist.value)
|
const editSmartPlaylist = () => eventBus.emit('MODAL_SHOW_EDIT_SMART_PLAYLIST_FORM', playlist.value)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,9 +101,8 @@ const editSmartPlaylist = () => eventBus.emit('MODAL_SHOW_EDIT_SMART_PLAYLIST_FO
|
||||||
const populate = async (_playlist: Playlist) => {
|
const populate = async (_playlist: Playlist) => {
|
||||||
await playlistStore.fetchSongs(_playlist)
|
await playlistStore.fetchSongs(_playlist)
|
||||||
playlist.value = _playlist
|
playlist.value = _playlist
|
||||||
state.songs = playlist.value.songs
|
songs.value = playlist.value.songs
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// @ts-ignore
|
|
||||||
songList.value?.sort()
|
songList.value?.sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +113,7 @@ eventBus.on('LOAD_MAIN_CONTENT', (view: MainViewName, _playlist: Playlist): void
|
||||||
|
|
||||||
if (_playlist.populated) {
|
if (_playlist.populated) {
|
||||||
playlist.value = _playlist
|
playlist.value = _playlist
|
||||||
state.songs = playlist.value.songs
|
songs.value = playlist.value.songs
|
||||||
} else {
|
} else {
|
||||||
populate(_playlist)
|
populate(_playlist)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ProfileForm = defineAsyncComponent(() => import('@/components/profile-preferences/profile-form.vue'))
|
const ProfileForm = defineAsyncComponent(() => import('@/components/profile-preferences/profile-form.vue'))
|
||||||
const LastfmIntegration = defineAsyncComponent(() => import('@/components/profile-preferences/lastfm-integration.vue'))
|
const LastfmIntegration = defineAsyncComponent(() => import('@/components/profile-preferences/lastfm-integration.vue'))
|
||||||
const Preferences = defineAsyncComponent(() => import('@/components/profile-preferences/preferences.vue'))
|
const Preferences = defineAsyncComponent(() => import('@/components/profile-preferences/preferences.vue'))
|
||||||
|
|
|
@ -46,19 +46,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, reactive, toRef } from 'vue'
|
||||||
import { pluralize } from '@/utils'
|
import { pluralize } from '@/utils'
|
||||||
import { queueStore, songStore } from '@/stores'
|
import { queueStore, songStore } from '@/stores'
|
||||||
import { playback } from '@/services'
|
import { playback } from '@/services'
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { computed, defineAsyncComponent, reactive, toRef } from 'vue'
|
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
|
@ -67,19 +68,13 @@ const {
|
||||||
isPhone,
|
isPhone,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList({
|
} = useSongList(toRef(queueStore.state, 'songs'), { clearQueue: true })
|
||||||
clearQueue: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const songs = toRef(queueStore.state, 'songs')
|
|
||||||
const songState = reactive(songStore.state)
|
const songState = reactive(songStore.state)
|
||||||
|
|
||||||
const shouldShowShufflingAllLink = computed(() => songState.songs.length > 0)
|
const shouldShowShufflingAllLink = computed(() => songState.songs.length > 0)
|
||||||
|
|
||||||
const playAll = () => {
|
const playAll = () => playback.queueAndPlay(songs.value.length ? songList.value.getAllSongsWithSort() : songStore.all)
|
||||||
playback.queueAndPlay(songs.value.length ? songList.value.getAllSongsWithSort() : songStore.all)
|
|
||||||
}
|
|
||||||
|
|
||||||
const shuffleAll = async () => await playback.queueAndPlay(songStore.all, true)
|
const shuffleAll = async () => await playback.queueAndPlay(songStore.all, true)
|
||||||
const clearQueue = () => queueStore.clear()
|
const clearQueue = () => queueStore.clear()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,26 +10,24 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="state.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
@playAll="playAll"
|
|
||||||
@playSelected="playSelected"
|
|
||||||
:songs="state.songs"
|
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
|
:songs="songs"
|
||||||
|
@playAll="playAll"
|
||||||
|
@playSelected="playSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList v-if="state.songs.length" :items="state.songs" type="recently-played" :sortable="false"/>
|
<SongList v-if="songs.length" :items="songs" :sortable="false" type="recently-played"/>
|
||||||
|
|
||||||
<ScreenPlaceholder v-else>
|
<ScreenPlaceholder v-else>
|
||||||
<template v-slot:icon>
|
<template v-slot:icon>
|
||||||
<i class="fa fa-clock-o"></i>
|
<i class="fa fa-clock-o"></i>
|
||||||
</template>
|
</template>
|
||||||
No songs recently played.
|
No songs recently played.
|
||||||
<span class="secondary d-block">
|
<span class="secondary d-block">Start playing to populate this playlist.</span>
|
||||||
Start playing to populate this playlist.
|
|
||||||
</span>
|
|
||||||
</ScreenPlaceholder>
|
</ScreenPlaceholder>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
@ -38,16 +36,17 @@
|
||||||
import { eventBus, pluralize } from '@/utils'
|
import { eventBus, pluralize } from '@/utils'
|
||||||
import { recentlyPlayedStore } from '@/stores'
|
import { recentlyPlayedStore } from '@/stores'
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { defineAsyncComponent, reactive } from 'vue'
|
import { defineAsyncComponent, reactive, toRef } from 'vue'
|
||||||
import { playback } from '@/services'
|
import { playback } from '@/services'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
|
@ -56,11 +55,9 @@ const {
|
||||||
isPhone,
|
isPhone,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(toRef(recentlyPlayedStore.state, 'songs'))
|
||||||
|
|
||||||
const state = reactive(recentlyPlayedStore.state)
|
const playAll = () => playback.queueAndPlay(songs.value)
|
||||||
|
|
||||||
const playAll = () => playback.queueAndPlay(state.songs)
|
|
||||||
|
|
||||||
eventBus.on({
|
eventBus.on({
|
||||||
'LOAD_MAIN_CONTENT': (view: MainViewName) => view === 'RecentlyPlayed' && recentlyPlayedStore.fetchAll()
|
'LOAD_MAIN_CONTENT': (view: MainViewName) => view === 'RecentlyPlayed' && recentlyPlayedStore.fetchAll()
|
||||||
|
|
|
@ -65,7 +65,7 @@ import { eventBus } from '@/utils'
|
||||||
import { searchStore } from '@/stores'
|
import { searchStore } from '@/stores'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
const SongCard = defineAsyncComponent(() => import('@/components/song/card.vue'))
|
const SongCard = defineAsyncComponent(() => import('@/components/song/card.vue'))
|
||||||
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
const ArtistCard = defineAsyncComponent(() => import('@/components/artist/card.vue'))
|
||||||
|
|
|
@ -10,34 +10,37 @@
|
||||||
|
|
||||||
<template v-slot:controls>
|
<template v-slot:controls>
|
||||||
<SongListControls
|
<SongListControls
|
||||||
v-if="state.songs.length && (!isPhone || showingControls)"
|
v-if="songs.length && (!isPhone || showingControls)"
|
||||||
:config="songListControlConfig"
|
:config="songListControlConfig"
|
||||||
:selectedSongs="selectedSongs"
|
:selectedSongs="selectedSongs"
|
||||||
:songs="state.songs"
|
:songs="songs"
|
||||||
@playAll="playAll"
|
@playAll="playAll"
|
||||||
@playSelected="playSelected"
|
@playSelected="playSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ScreenHeader>
|
</ScreenHeader>
|
||||||
|
|
||||||
<SongList ref="songList" :items="state.songs" type="search-results"/>
|
<SongList ref="songList" :items="songs" type="search-results"/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, toRef, toRefs } from 'vue'
|
||||||
import { searchStore } from '@/stores'
|
import { searchStore } from '@/stores'
|
||||||
import { computed, defineAsyncComponent, reactive, toRefs } from 'vue'
|
|
||||||
import { useSongList } from '@/composables'
|
import { useSongList } from '@/composables'
|
||||||
import { pluralize } from '@/utils'
|
import { pluralize } from '@/utils'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
|
|
||||||
|
const props = defineProps<{ q: string }>()
|
||||||
|
const { q } = toRefs(props)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
state: songListState,
|
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
showingControls,
|
showingControls,
|
||||||
|
@ -46,12 +49,7 @@ const {
|
||||||
playAll,
|
playAll,
|
||||||
playSelected,
|
playSelected,
|
||||||
toggleControls
|
toggleControls
|
||||||
} = useSongList()
|
} = useSongList(toRef(searchStore.state, 'songs'))
|
||||||
|
|
||||||
const props = defineProps<{ q: string }>()
|
|
||||||
const { q } = toRefs(props)
|
|
||||||
|
|
||||||
const state = reactive(searchStore.state)
|
|
||||||
|
|
||||||
const decodedQ = computed(() => decodeURIComponent(q.value))
|
const decodedQ = computed(() => decodeURIComponent(q.value))
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { settingStore, sharedStore } from '@/stores'
|
||||||
import { alerts, forceReloadWindow, hideOverlay, parseValidationError, showOverlay } from '@/utils'
|
import { alerts, forceReloadWindow, hideOverlay, parseValidationError, showOverlay } from '@/utils'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
||||||
|
|
||||||
const state = settingStore.state
|
const state = settingStore.state
|
||||||
|
|
|
@ -67,10 +67,10 @@ import { UploadFile, validMediaMimeTypes } from '@/config'
|
||||||
import { upload } from '@/services'
|
import { upload } from '@/services'
|
||||||
|
|
||||||
import UploadItem from '@/components/ui/upload/upload-item.vue'
|
import UploadItem from '@/components/ui/upload/upload-item.vue'
|
||||||
import BtnGroup from '@/components/ui/btn-group.vue'
|
import BtnGroup from '@/components/ui/BtnGroup.vue'
|
||||||
import Btn from '@/components/ui/btn.vue'
|
import Btn from '@/components/ui/btn.vue'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
const mediaPath = toRef(settingStore.state, 'media_path')
|
const mediaPath = toRef(settingStore.state, 'media_path')
|
||||||
|
|
|
@ -29,10 +29,10 @@ import isMobile from 'ismobilejs'
|
||||||
import { userStore } from '@/stores'
|
import { userStore } from '@/stores'
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ControlsToggler = defineAsyncComponent(() => import('@/components/ui/screen-controls-toggler.vue'))
|
const ControlsToggler = defineAsyncComponent(() => import('@/components/ui/ScreenControlsToggler.vue'))
|
||||||
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
||||||
const BtnGroup = defineAsyncComponent(() => import('@/components/ui/btn-group.vue'))
|
const BtnGroup = defineAsyncComponent(() => import('@/components/ui/BtnGroup.vue'))
|
||||||
const UserCard = defineAsyncComponent(() => import('@/components/user/card.vue'))
|
const UserCard = defineAsyncComponent(() => import('@/components/user/card.vue'))
|
||||||
|
|
||||||
const state = reactive(userStore.state)
|
const state = reactive(userStore.state)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import createYouTubePlayer from 'youtube-player'
|
||||||
|
|
||||||
let player: YouTubePlayer|null = null
|
let player: YouTubePlayer|null = null
|
||||||
|
|
||||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/ScreenHeader.vue'))
|
||||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||||
|
|
||||||
const title = ref('YouTube Video')
|
const title = ref('YouTube Video')
|
||||||
|
|
|
@ -72,7 +72,6 @@ import {
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
nextTick,
|
nextTick,
|
||||||
onMounted,
|
onMounted,
|
||||||
PropType,
|
|
||||||
ref,
|
ref,
|
||||||
toRefs,
|
toRefs,
|
||||||
watch
|
watch
|
||||||
|
@ -182,7 +181,8 @@ const render = () => {
|
||||||
|
|
||||||
watch(items, () => render())
|
watch(items, () => render())
|
||||||
|
|
||||||
watch(selectedSongs, () => eventBus.emit('SET_SELECTED_SONGS', selectedSongs, getCurrentInstance()?.parent))
|
const vm = getCurrentInstance()
|
||||||
|
watch(selectedSongs, () => eventBus.emit('SET_SELECTED_SONGS', selectedSongs.value, vm?.parent))
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
if (!selectedSongs.value.length) {
|
if (!selectedSongs.value.length) {
|
|
@ -4,23 +4,23 @@
|
||||||
<template v-if="mergedConfig.play">
|
<template v-if="mergedConfig.play">
|
||||||
<template v-if="altPressed">
|
<template v-if="altPressed">
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="playAll"
|
v-if="selectedSongs.length < 2 && songs.length"
|
||||||
class="btn-play-all"
|
class="btn-play-all"
|
||||||
|
data-test="btn-play-all"
|
||||||
orange
|
orange
|
||||||
title="Play all songs"
|
title="Play all songs"
|
||||||
v-if="selectedSongs.length < 2 && songs.length"
|
@click.prevent="playAll"
|
||||||
data-test="btn-play-all"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-play"></i> All
|
<i class="fa fa-play"></i> All
|
||||||
</Btn>
|
</Btn>
|
||||||
|
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="playSelected"
|
v-if="selectedSongs.length > 1"
|
||||||
class="btn-play-selected"
|
class="btn-play-selected"
|
||||||
|
data-test="btn-play-selected"
|
||||||
orange
|
orange
|
||||||
title="Play selected songs"
|
title="Play selected songs"
|
||||||
v-if="selectedSongs.length > 1"
|
@click.prevent="playSelected"
|
||||||
data-test="btn-play-selected"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-play"></i> Selected
|
<i class="fa fa-play"></i> Selected
|
||||||
</Btn>
|
</Btn>
|
||||||
|
@ -28,23 +28,23 @@
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="shuffle"
|
v-if="selectedSongs.length < 2 && songs.length"
|
||||||
class="btn-shuffle-all"
|
class="btn-shuffle-all"
|
||||||
|
data-test="btn-shuffle-all"
|
||||||
orange
|
orange
|
||||||
title="Shuffle all songs"
|
title="Shuffle all songs"
|
||||||
v-if="selectedSongs.length < 2 && songs.length"
|
@click.prevent="shuffle"
|
||||||
data-test="btn-shuffle-all"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-random"></i> All
|
<i class="fa fa-random"></i> All
|
||||||
</Btn>
|
</Btn>
|
||||||
|
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="shuffleSelected"
|
v-if="selectedSongs.length > 1"
|
||||||
class="btn-shuffle-selected"
|
class="btn-shuffle-selected"
|
||||||
|
data-test="btn-shuffle-selected"
|
||||||
orange
|
orange
|
||||||
title="Shuffle selected songs"
|
title="Shuffle selected songs"
|
||||||
v-if="selectedSongs.length > 1"
|
@click.prevent="shuffleSelected"
|
||||||
data-test="btn-shuffle-selected"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-random"></i> Selected
|
<i class="fa fa-random"></i> Selected
|
||||||
</Btn>
|
</Btn>
|
||||||
|
@ -63,21 +63,21 @@
|
||||||
</Btn>
|
</Btn>
|
||||||
|
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="clearQueue"
|
v-if="showClearQueueButton"
|
||||||
class="btn-clear-queue"
|
class="btn-clear-queue"
|
||||||
red
|
red
|
||||||
v-if="showClearQueueButton"
|
|
||||||
title="Clear current queue"
|
title="Clear current queue"
|
||||||
|
@click.prevent="clearQueue"
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</Btn>
|
</Btn>
|
||||||
|
|
||||||
<Btn
|
<Btn
|
||||||
@click.prevent="deletePlaylist"
|
v-if="showDeletePlaylistButton"
|
||||||
class="del btn-delete-playlist"
|
class="del btn-delete-playlist"
|
||||||
red
|
red
|
||||||
title="Delete this playlist"
|
title="Delete this playlist"
|
||||||
v-if="showDeletePlaylistButton"
|
@click.prevent="deletePlaylist"
|
||||||
>
|
>
|
||||||
<i class="fa fa-times"></i> Playlist
|
<i class="fa fa-times"></i> Playlist
|
||||||
</Btn>
|
</Btn>
|
||||||
|
@ -85,11 +85,11 @@
|
||||||
</BtnGroup>
|
</BtnGroup>
|
||||||
|
|
||||||
<AddToMenu
|
<AddToMenu
|
||||||
@closing="closeAddToMenu"
|
|
||||||
:config="mergedConfig.addTo"
|
|
||||||
:songs="selectedSongs"
|
|
||||||
:showing="showingAddToMenu"
|
|
||||||
v-koel-clickaway="closeAddToMenu"
|
v-koel-clickaway="closeAddToMenu"
|
||||||
|
:config="mergedConfig.addTo"
|
||||||
|
:showing="showingAddToMenu"
|
||||||
|
:songs="selectedSongs"
|
||||||
|
@closing="closeAddToMenu"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -99,17 +99,20 @@ import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref,
|
||||||
|
|
||||||
const AddToMenu = defineAsyncComponent(() => import('./AddToMenu.vue'))
|
const AddToMenu = defineAsyncComponent(() => import('./AddToMenu.vue'))
|
||||||
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
||||||
const BtnGroup = defineAsyncComponent(() => import('@/components/ui/btn-group.vue'))
|
const BtnGroup = defineAsyncComponent(() => import('@/components/ui/BtnGroup.vue'))
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{ songs: Song[], selectedSongs: Song[], config: Partial<SongListControlsConfig> }>(), {
|
const props = withDefaults(
|
||||||
|
defineProps<{ songs: Song[], selectedSongs: Song[], config: Partial<SongListControlsConfig> }>(),
|
||||||
|
{
|
||||||
songs: () => [],
|
songs: () => [],
|
||||||
selectedSongs: () => [],
|
selectedSongs: () => [],
|
||||||
config: () => ({})
|
config: () => ({})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const { config, songs, selectedSongs } = toRefs(props)
|
const { config, songs, selectedSongs } = toRefs(props)
|
||||||
|
|
||||||
const el = ref(null as unknown as HTMLElement)
|
const el = ref<HTMLElement>()
|
||||||
const showingAddToMenu = ref(false)
|
const showingAddToMenu = ref(false)
|
||||||
const numberOfQueuedSongs = ref(0)
|
const numberOfQueuedSongs = ref(0)
|
||||||
const altPressed = ref(false)
|
const altPressed = ref(false)
|
||||||
|
@ -124,7 +127,7 @@ const mergedConfig = computed((): SongListControlsConfig => Object.assign({
|
||||||
},
|
},
|
||||||
clearQueue: false,
|
clearQueue: false,
|
||||||
deletePlaylist: false
|
deletePlaylist: false
|
||||||
}, config)
|
}, config.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
const showClearQueueButton = computed(() => mergedConfig.value.clearQueue)
|
const showClearQueueButton = computed(() => mergedConfig.value.clearQueue)
|
||||||
|
@ -151,9 +154,9 @@ const toggleAddToMenu = async () => {
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
const btnAddTo = el.value.querySelector<HTMLButtonElement>('.btn-add-to')!
|
const btnAddTo = el.value?.querySelector<HTMLButtonElement>('.btn-add-to')!
|
||||||
const { left: btnLeft, bottom: btnBottom, width: btnWidth } = btnAddTo.getBoundingClientRect()
|
const { left: btnLeft, bottom: btnBottom, width: btnWidth } = btnAddTo.getBoundingClientRect()
|
||||||
const contextMenu = el.value.querySelector<HTMLElement>('.add-to')!
|
const contextMenu = el.value?.querySelector<HTMLElement>('.add-to')!
|
||||||
const menuWidth = contextMenu.getBoundingClientRect().width
|
const menuWidth = contextMenu.getBoundingClientRect().width
|
||||||
contextMenu.style.top = `${btnBottom + 10}px`
|
contextMenu.style.top = `${btnBottom + 10}px`
|
||||||
contextMenu.style.left = `${btnLeft + btnWidth / 2 - menuWidth / 2}px`
|
contextMenu.style.left = `${btnLeft + btnWidth / 2 - menuWidth / 2}px`
|
|
@ -14,7 +14,7 @@ import { defineAsyncComponent } from 'vue'
|
||||||
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
const Btn = defineAsyncComponent(() => import('@/components/ui/btn.vue'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.btn-group {
|
.btn-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
|
@ -1,20 +1,20 @@
|
||||||
/**
|
/**
|
||||||
* Add necessary functionalities into a view that contains a song-list component.
|
* Add necessary functionalities into a view that contains a song-list component.
|
||||||
*/
|
*/
|
||||||
import { ComponentInternalInstance, getCurrentInstance, reactive, ref, watchEffect } from 'vue'
|
import { ComponentInternalInstance, getCurrentInstance, reactive, Ref, ref, watchEffect } from 'vue'
|
||||||
import isMobile from 'ismobilejs'
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { playback } from '@/services'
|
import { playback } from '@/services'
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
|
|
||||||
import ControlsToggler from '@/components/ui/screen-controls-toggler.vue'
|
import ControlsToggler from '@/components/ui/ScreenControlsToggler.vue'
|
||||||
import SongList from '@/components/song/list.vue'
|
import SongList from '@/components/song/SongList.vue'
|
||||||
import SongListControls from '@/components/song/list-controls.vue'
|
import SongListControls from '@/components/song/SongListControls.vue'
|
||||||
import { songStore } from '@/stores'
|
import { songStore } from '@/stores'
|
||||||
|
|
||||||
export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}) => {
|
export const useSongList = (songs: Ref<Song[]>, controlsConfig: Partial<SongListControlsConfig> = {}) => {
|
||||||
const songList = ref<InstanceType<typeof SongList>>()
|
const songList = ref<InstanceType<typeof SongList>>()
|
||||||
const state = reactive<SongListState>({ songs: [] })
|
const vm = getCurrentInstance()
|
||||||
|
|
||||||
const meta = reactive<SongListMeta>({
|
const meta = reactive<SongListMeta>({
|
||||||
songCount: 0,
|
songCount: 0,
|
||||||
|
@ -27,12 +27,12 @@ export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}
|
||||||
const isPhone = isMobile.phone
|
const isPhone = isMobile.phone
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (!state.songs.length) {
|
if (!songs.value.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.songCount = state.songs.length
|
meta.songCount = songs.value.length
|
||||||
meta.totalLength = songStore.getFormattedLength(state.songs)
|
meta.totalLength = songStore.getFormattedLength(songs.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const getSongsToPlay = (): Song[] => songList.value.getAllSongsWithSort()
|
const getSongsToPlay = (): Song[] => songList.value.getAllSongsWithSort()
|
||||||
|
@ -42,7 +42,7 @@ export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}
|
||||||
|
|
||||||
eventBus.on({
|
eventBus.on({
|
||||||
SET_SELECTED_SONGS (songs: Song[], target: ComponentInternalInstance) {
|
SET_SELECTED_SONGS (songs: Song[], target: ComponentInternalInstance) {
|
||||||
target === getCurrentInstance() && (selectedSongs.value = songs)
|
target === vm && (selectedSongs.value = songs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}
|
||||||
SongList,
|
SongList,
|
||||||
SongListControls,
|
SongListControls,
|
||||||
ControlsToggler,
|
ControlsToggler,
|
||||||
|
songs,
|
||||||
songList,
|
songList,
|
||||||
state,
|
|
||||||
meta,
|
meta,
|
||||||
selectedSongs,
|
selectedSongs,
|
||||||
showingControls,
|
showingControls,
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { difference, union } from 'lodash'
|
import { difference, union } from 'lodash'
|
||||||
import { http } from '@/services'
|
import { http } from '@/services'
|
||||||
import { arrayify } from '@/utils'
|
import { arrayify } from '@/utils'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
export const favoriteStore = {
|
export const favoriteStore = {
|
||||||
state: {
|
state: reactive({
|
||||||
songs: [] as Song[],
|
songs: [] as Song[],
|
||||||
length: 0,
|
length: 0,
|
||||||
fmtLength: ''
|
fmtLength: ''
|
||||||
},
|
}),
|
||||||
|
|
||||||
get all (): Song[] {
|
get all () {
|
||||||
return this.state.songs
|
return this.state.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ export const favoriteStore = {
|
||||||
this.state.songs = value
|
this.state.songs = value
|
||||||
},
|
},
|
||||||
|
|
||||||
async toggleOne (song: Song): Promise<void> {
|
async toggleOne (song: Song) {
|
||||||
// Don't wait for the HTTP response to update the status, just toggle right away.
|
// Don't wait for the HTTP response to update the status, just toggle right away.
|
||||||
// This may cause a minor problem if the request fails somehow, but do we care?
|
// This may cause a minor problem if the request fails somehow, but do we care?
|
||||||
song.liked = !song.liked
|
song.liked = !song.liked
|
||||||
|
@ -26,17 +27,11 @@ export const favoriteStore = {
|
||||||
await http.post<Song>('interaction/like', { song: song.id })
|
await http.post<Song>('interaction/like', { song: song.id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
add (songs: Song | Song[]) {
|
||||||
* Add a song/songs into the store.
|
|
||||||
*/
|
|
||||||
add (songs: Song | Song[]): void {
|
|
||||||
this.all = union(this.all, arrayify(songs))
|
this.all = union(this.all, arrayify(songs))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
remove (songs: Song | Song[]) {
|
||||||
* Remove a song/songs from the store.
|
|
||||||
*/
|
|
||||||
remove (songs: Song | Song[]): void {
|
|
||||||
this.all = difference(this.all, arrayify(songs))
|
this.all = difference(this.all, arrayify(songs))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -44,7 +39,7 @@ export const favoriteStore = {
|
||||||
this.all = []
|
this.all = []
|
||||||
},
|
},
|
||||||
|
|
||||||
async like (songs: Song[]): Promise<void> {
|
async like (songs: Song[]) {
|
||||||
// Don't wait for the HTTP response to update the status, just set them to Liked right away.
|
// Don't wait for the HTTP response to update the status, just set them to Liked right away.
|
||||||
// This may cause a minor problem if the request fails somehow, but do we care?
|
// This may cause a minor problem if the request fails somehow, but do we care?
|
||||||
songs.forEach(song => { song.liked = true })
|
songs.forEach(song => { song.liked = true })
|
||||||
|
@ -53,7 +48,7 @@ export const favoriteStore = {
|
||||||
await http.post('interaction/batch/like', { songs: songs.map(song => song.id) })
|
await http.post('interaction/batch/like', { songs: songs.map(song => song.id) })
|
||||||
},
|
},
|
||||||
|
|
||||||
async unlike (songs: Song[]): Promise<void> {
|
async unlike (songs: Song[]) {
|
||||||
songs.forEach(song => { song.liked = false })
|
songs.forEach(song => { song.liked = false })
|
||||||
this.remove(songs)
|
this.remove(songs)
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
import { songStore } from '.'
|
import { songStore } from '.'
|
||||||
import { http } from '@/services'
|
import { http } from '@/services'
|
||||||
import { remove } from 'lodash'
|
import { remove } from 'lodash'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
const EXCERPT_COUNT = 7
|
const EXCERPT_COUNT = 7
|
||||||
|
|
||||||
export const recentlyPlayedStore = {
|
export const recentlyPlayedStore = {
|
||||||
excerptState: {
|
excerptState: reactive({
|
||||||
songs: [] as Song[]
|
songs: [] as Song[]
|
||||||
},
|
}),
|
||||||
|
|
||||||
state: {
|
state: reactive({
|
||||||
songs: [] as Song[]
|
songs: [] as Song[]
|
||||||
},
|
}),
|
||||||
|
|
||||||
fetched: false,
|
fetched: false,
|
||||||
|
|
||||||
initExcerpt (songIds: string[]): void {
|
initExcerpt (songIds: string[]) {
|
||||||
this.excerptState.songs = songStore.byIds(songIds)
|
this.excerptState.songs = songStore.byIds(songIds)
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchAll (): Promise<Song[]> {
|
async fetchAll () {
|
||||||
if (!this.fetched) {
|
if (!this.fetched) {
|
||||||
this.state.songs = songStore.byIds(await http.get<string[]>(`interaction/recently-played`))
|
this.state.songs = songStore.byIds(await http.get<string[]>(`interaction/recently-played`))
|
||||||
this.fetched = true
|
this.fetched = true
|
||||||
|
@ -28,8 +29,8 @@ export const recentlyPlayedStore = {
|
||||||
return this.state.songs
|
return this.state.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
add (song: Song): void {
|
add (song: Song) {
|
||||||
[this.state, this.excerptState].forEach((state): void => {
|
[this.state, this.excerptState].forEach(state => {
|
||||||
// make sure there's no duplicate
|
// make sure there's no duplicate
|
||||||
remove(state.songs, s => s.id === song.id)
|
remove(state.songs, s => s.id === song.id)
|
||||||
state.songs.unshift(song)
|
state.songs.unshift(song)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { http } from '@/services'
|
||||||
import { songStore } from '@/stores/song'
|
import { songStore } from '@/stores/song'
|
||||||
import { albumStore } from '@/stores/album'
|
import { albumStore } from '@/stores/album'
|
||||||
import { artistStore } from '@/stores/artist'
|
import { artistStore } from '@/stores/artist'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
interface ExcerptSearchResult {
|
interface ExcerptSearchResult {
|
||||||
songs: Array<string>
|
songs: Array<string>
|
||||||
|
@ -14,27 +15,25 @@ interface SongSearchResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const searchStore = {
|
export const searchStore = {
|
||||||
state: {
|
state: reactive({
|
||||||
excerpt: {
|
excerpt: {
|
||||||
songs: [] as Song[],
|
songs: [] as Song[],
|
||||||
albums: [] as Album[],
|
albums: [] as Album[],
|
||||||
artists: [] as Artist[]
|
artists: [] as Artist[]
|
||||||
},
|
},
|
||||||
songs: [] as Song[],
|
songs: [] as Song[]
|
||||||
},
|
}),
|
||||||
|
|
||||||
excerptSearch (q: string) {
|
async excerptSearch (q: string) {
|
||||||
http.get<{ [key: string]: ExcerptSearchResult }>(`search?q=${q}`).then(({ results }) => {
|
const { results } = await http.get<{ [key: string]: ExcerptSearchResult }>(`search?q=${q}`)
|
||||||
this.state.excerpt.songs = songStore.byIds(results.songs)
|
this.state.excerpt.songs = songStore.byIds(results.songs)
|
||||||
this.state.excerpt.albums = albumStore.byIds(results.albums)
|
this.state.excerpt.albums = albumStore.byIds(results.albums)
|
||||||
this.state.excerpt.artists = artistStore.byIds(results.artists)
|
this.state.excerpt.artists = artistStore.byIds(results.artists)
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
songSearch (q: string) {
|
async songSearch (q: string) {
|
||||||
http.get<SongSearchResult>(`search/songs?q=${q}`).then(({ songs }) => {
|
const { songs } = await http.get<SongSearchResult>(`search/songs?q=${q}`)
|
||||||
this.state.songs = this.state.songs.concat(songStore.byIds(songs))
|
this.state.songs = this.state.songs.concat(songStore.byIds(songs))
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
resetSongResultState () {
|
resetSongResultState () {
|
||||||
|
|
Loading…
Reference in a new issue