mirror of
https://github.com/koel/koel
synced 2024-11-28 06:50:27 +00:00
feat: do not manually emit selected songs
This commit is contained in:
parent
9ae60b90ea
commit
d59c0c8bfe
9 changed files with 23 additions and 35 deletions
|
@ -2,18 +2,12 @@ import lodash from 'lodash'
|
|||
import factory from '@/__tests__/factory'
|
||||
import { expect, it } from 'vitest'
|
||||
import { fireEvent } from '@testing-library/vue'
|
||||
import { eventBus, noop } from '@/utils'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import SongList from './SongList.vue'
|
||||
|
||||
let songs: Song[]
|
||||
|
||||
new class extends UnitTestCase {
|
||||
protected beforeEach () {
|
||||
// suppress the warning
|
||||
super.beforeEach(() => eventBus.on('SET_SELECTED_SONGS', noop))
|
||||
}
|
||||
|
||||
private renderComponent (type: SongListType = 'all-songs') {
|
||||
songs = factory<Song>('song', 3)
|
||||
|
||||
|
|
|
@ -85,9 +85,9 @@
|
|||
<script lang="ts" setup>
|
||||
import isMobile from 'ismobilejs'
|
||||
import { findIndex } from 'lodash'
|
||||
import { computed, defineAsyncComponent, getCurrentInstance, inject, onMounted, ref, watch } from 'vue'
|
||||
import { computed, defineAsyncComponent, inject, onMounted, ref, watch } from 'vue'
|
||||
import { $, eventBus, startDragging } from '@/utils'
|
||||
import { SongListConfigKey, SongListTypeKey, SongsKey } from '@/symbols'
|
||||
import { SelectedSongsKey, SongListConfigKey, SongListTypeKey, SongsKey } from '@/symbols'
|
||||
|
||||
const VirtualScroller = defineAsyncComponent(() => import('@/components/ui/VirtualScroller.vue'))
|
||||
const SongListItem = defineAsyncComponent(() => import('@/components/song/SongListItem.vue'))
|
||||
|
@ -96,6 +96,7 @@ const emit = defineEmits(['press:enter', 'press:delete', 'reorder', 'sort', 'scr
|
|||
|
||||
const items = inject(SongsKey, ref([]))
|
||||
const type = inject(SongListTypeKey, 'all-songs')
|
||||
const selectedSongs = inject(SelectedSongsKey, ref([]))
|
||||
|
||||
const lastSelectedRow = ref<SongRow>()
|
||||
const sortFields = ref<SongListSortField[]>([])
|
||||
|
@ -103,7 +104,10 @@ const sortOrder = ref<SortOrder>('asc')
|
|||
const songRows = ref<SongRow[]>([])
|
||||
|
||||
const allowReordering = type === 'queue'
|
||||
const selectedSongs = computed(() => songRows.value.filter(row => row.selected).map(row => row.song))
|
||||
|
||||
watch(songRows, () => {
|
||||
selectedSongs.value = songRows.value.filter(row => row.selected).map(row => row.song)
|
||||
}, { deep: true })
|
||||
|
||||
const config = computed((): SongListConfig => {
|
||||
return Object.assign({
|
||||
|
@ -113,7 +117,7 @@ const config = computed((): SongListConfig => {
|
|||
})
|
||||
|
||||
const currentSortField = ref<SongListSortField | null>((() => {
|
||||
if (['album', 'artist'].includes(type)) return 'track'
|
||||
if (type === 'album' || type === 'artist') return 'track'
|
||||
if (type === 'search-results') return null
|
||||
return config.value.sortable ? 'title' : null
|
||||
})())
|
||||
|
@ -153,9 +157,6 @@ const render = () => {
|
|||
|
||||
watch(items, () => render(), { deep: true })
|
||||
|
||||
const vm = getCurrentInstance()
|
||||
watch(selectedSongs, () => eventBus.emit('SET_SELECTED_SONGS', selectedSongs.value, vm?.parent))
|
||||
|
||||
const handleDelete = () => {
|
||||
emit('press:delete')
|
||||
clearSelection()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { ComponentInternalInstance, computed, getCurrentInstance, provide, reactive, Ref, ref } from 'vue'
|
||||
import { computed, getCurrentInstance, provide, reactive, Ref, ref } from 'vue'
|
||||
import isMobile from 'ismobilejs'
|
||||
import { orderBy } from 'lodash'
|
||||
|
||||
import { playbackService } from '@/services'
|
||||
import { eventBus } from '@/utils'
|
||||
import { queueStore, songStore } from '@/stores'
|
||||
import router from '@/router'
|
||||
|
||||
|
@ -67,12 +66,6 @@ export const useSongList = (
|
|||
songs.value = orderBy(songs.value, sortFields, sortOrder)
|
||||
}
|
||||
|
||||
eventBus.on({
|
||||
SET_SELECTED_SONGS (songs: Song[], target: ComponentInternalInstance) {
|
||||
target === vm && (selectedSongs.value = songs)
|
||||
}
|
||||
})
|
||||
|
||||
provide(SongListTypeKey, type)
|
||||
provide(SongsKey, songs)
|
||||
provide(SelectedSongsKey, selectedSongs)
|
||||
|
|
|
@ -9,7 +9,6 @@ export type EventName =
|
|||
| 'PLAY_YOUTUBE_VIDEO'
|
||||
| 'INIT_EQUALIZER'
|
||||
| 'TOGGLE_VISUALIZER'
|
||||
| 'SET_SELECTED_SONGS'
|
||||
| 'SEARCH_KEYWORDS_CHANGED'
|
||||
| 'SONG_CONTEXT_MENU_REQUESTED'
|
||||
| 'ALBUM_CONTEXT_MENU_REQUESTED'
|
||||
|
|
|
@ -41,10 +41,10 @@ export const Cache = {
|
|||
this.storage.delete(this.normalizeKey(key))
|
||||
},
|
||||
|
||||
resolve<T> (key: any, resolver: Closure) {
|
||||
async resolve<T> (key: any, fetcher: Closure) {
|
||||
key = this.normalizeKey(key)
|
||||
|
||||
this.hit(key) || this.set(key, resolver())
|
||||
this.hit(key) || this.set(key, await fetcher())
|
||||
return this.storage.get(key)!.value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export const albumStore = {
|
|||
let album = this.byId(id)
|
||||
|
||||
if (!album) {
|
||||
album = Cache.resolve<Album>(['album', id], async () => await httpService.get<Album>(`albums/${id}`))
|
||||
album = await Cache.resolve<Album>(['album', id], async () => await httpService.get<Album>(`albums/${id}`))
|
||||
this.syncWithVault(album)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,9 @@ export const artistStore = {
|
|||
ids.forEach(id => this.vault.delete(id))
|
||||
},
|
||||
|
||||
isVarious: (artist: Artist | number) => {
|
||||
if (typeof artist === 'number') return artist === VARIOUS_ARTISTS_ID
|
||||
return artist.id === VARIOUS_ARTISTS_ID
|
||||
},
|
||||
isVarious: (artist: Artist | number) => (typeof artist === 'number')
|
||||
? artist === VARIOUS_ARTISTS_ID
|
||||
: artist.id === VARIOUS_ARTISTS_ID,
|
||||
|
||||
isUnknown: (artist: Artist | number) => (typeof artist === 'number')
|
||||
? artist === UNKNOWN_ARTIST_ID
|
||||
|
@ -56,7 +55,7 @@ export const artistStore = {
|
|||
let artist = this.byId(id)
|
||||
|
||||
if (!artist) {
|
||||
artist = Cache.resolve<Artist>(['artist', id], async () => await httpService.get<Artist>(`artists/${id}`))
|
||||
artist = await Cache.resolve<Artist>(['artist', id], async () => await httpService.get<Artist>(`artists/${id}`))
|
||||
this.syncWithVault(artist)
|
||||
}
|
||||
|
||||
|
|
|
@ -180,21 +180,21 @@ export const songStore = {
|
|||
},
|
||||
|
||||
async fetchForAlbum (album: Album) {
|
||||
return Cache.resolve<Song[]>(
|
||||
return await Cache.resolve<Song[]>(
|
||||
[`album.songs`, album.id],
|
||||
async () => this.syncWithVault(await httpService.get<Song[]>(`albums/${album.id}/songs`))
|
||||
)
|
||||
},
|
||||
|
||||
async fetchForArtist (artist: Artist) {
|
||||
return Cache.resolve<Song[]>(
|
||||
return await Cache.resolve<Song[]>(
|
||||
['artist.songs', artist.id],
|
||||
async () => this.syncWithVault(await httpService.get<Song[]>(`artists/${artist.id}/songs`))
|
||||
)
|
||||
},
|
||||
|
||||
async fetchForPlaylist (playlist: Playlist) {
|
||||
return Cache.resolve<Song[]>(
|
||||
return await Cache.resolve<Song[]>(
|
||||
[`playlist.songs`, playlist.id],
|
||||
async () => this.syncWithVault(await httpService.get<Song[]>(`playlists/${playlist.id}/songs`))
|
||||
)
|
||||
|
|
|
@ -44,5 +44,7 @@ export const slugToTitle = (slug: string, separator = '-') => {
|
|||
return title.replace(/\s+/g, ' ').trim()
|
||||
}
|
||||
|
||||
export const pluralize = (count: number, singular: string) =>
|
||||
count === 1 ? `${count} ${singular}` : `${count.toLocaleString()} ${singular}s`
|
||||
export const pluralize = (count: number | undefined, singular: string) => {
|
||||
count = count ?? 0
|
||||
return count === 1 ? `${count} ${singular}` : `${count.toLocaleString()} ${singular}s`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue