fix: song controls disappear during filtering (#1864)

This commit is contained in:
Phan An 2024-10-28 15:19:32 +07:00 committed by GitHub
parent 167494ce6d
commit 63f2dc5241
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 32 additions and 17 deletions

View file

@ -66,7 +66,7 @@ export default abstract class UnitTestCase {
commonStore.state.uses_i_tunes = true commonStore.state.uses_i_tunes = true
commonStore.state.supports_batch_downloading = true commonStore.state.supports_batch_downloading = true
commonStore.state.supports_transcoding = true commonStore.state.supports_transcoding = true
cb && cb() cb?.()
}) })
} }
@ -77,7 +77,7 @@ export default abstract class UnitTestCase {
cleanup() cleanup()
this.restoreAllMocks() this.restoreAllMocks()
this.disablePlusEdition() this.disablePlusEdition()
cb && cb() cb?.()
}) })
} }

View file

@ -11,7 +11,7 @@ export default (faker: Faker): Podcast => {
description: faker.lorem.paragraph(), description: faker.lorem.paragraph(),
author: faker.name.findName(), author: faker.name.findName(),
subscribed_at: faker.date.past().toISOString(), subscribed_at: faker.date.past().toISOString(),
created_at: faker.date.past().toISOString(), last_played_at: faker.date.past().toISOString(),
state: { state: {
current_episode: null, current_episode: null,
progresses: {}, progresses: {},

View file

@ -194,6 +194,11 @@ watch(playlistId, async id => {
// reset this config value to its default to not cause rows to be mal-rendered // reset this config value to its default to not cause rows to be mal-rendered
listConfig.collaborative = false listConfig.collaborative = false
// Since this component is responsible for all playlists, reset these values
// so that they're not shared between lists
songs.value = []
selectedPlayables.value = []
if (playlist.value) { if (playlist.value) {
await fetchDetails() await fetchDetails()
listConfig.collaborative = playlist.value.is_collaborative listConfig.collaborative = playlist.value.is_collaborative

View file

@ -4,10 +4,10 @@ import UnitTestCase from '@/__tests__/UnitTestCase'
import factory from '@/__tests__/factory' import factory from '@/__tests__/factory'
import { arrayify } from '@/utils/helpers' import { arrayify } from '@/utils/helpers'
import { import {
FilteredPlayablesKey,
PlayableListConfigKey, PlayableListConfigKey,
PlayableListContextKey, PlayableListContextKey,
PlayableListSortFieldKey, PlayableListSortFieldKey,
PlayablesKey,
SelectedPlayablesKey, SelectedPlayablesKey,
SongListSortOrderKey, SongListSortOrderKey,
} from '@/symbols' } from '@/symbols'
@ -54,7 +54,7 @@ new class extends UnitTestCase {
SongListHeader: this.stub('song-list-header'), SongListHeader: this.stub('song-list-header'),
}, },
provide: { provide: {
[<symbol>PlayablesKey]: [ref(songs)], [<symbol>FilteredPlayablesKey]: [ref(songs)],
[<symbol>SelectedPlayablesKey]: [ref(selectedPlayables), (value: Playable[]) => (selectedPlayables = value)], [<symbol>SelectedPlayablesKey]: [ref(selectedPlayables), (value: Playable[]) => (selectedPlayables = value)],
[<symbol>PlayableListConfigKey]: [config], [<symbol>PlayableListConfigKey]: [config],
[<symbol>PlayableListContextKey]: [context], [<symbol>PlayableListContextKey]: [context],

View file

@ -48,10 +48,10 @@ import { queueStore } from '@/stores/queueStore'
import { useDraggable, useDroppable } from '@/composables/useDragAndDrop' import { useDraggable, useDroppable } from '@/composables/useDragAndDrop'
import { playbackService } from '@/services/playbackService' import { playbackService } from '@/services/playbackService'
import { import {
FilteredPlayablesKey,
PlayableListConfigKey, PlayableListConfigKey,
PlayableListContextKey, PlayableListContextKey,
PlayableListSortFieldKey, PlayableListSortFieldKey,
PlayablesKey,
SelectedPlayablesKey, SelectedPlayablesKey,
} from '@/symbols' } from '@/symbols'
@ -71,7 +71,7 @@ const emit = defineEmits<{
const { startDragging } = useDraggable('playables') const { startDragging } = useDraggable('playables')
const { getDroppedData, acceptsDrop } = useDroppable(['playables']) const { getDroppedData, acceptsDrop } = useDroppable(['playables'])
const [playables] = requireInjection<[Ref<Playable[]>]>(PlayablesKey) const [playables] = requireInjection<[Ref<Playable[]>]>(FilteredPlayablesKey)
const [selectedPlayables, setSelectedPlayables] = requireInjection<[Ref<Playable[]>, Closure]>(SelectedPlayablesKey) const [selectedPlayables, setSelectedPlayables] = requireInjection<[Ref<Playable[]>, Closure]>(SelectedPlayablesKey)
const [sortField] = requireInjection<[Ref<MaybeArray<PlayableListSortField>>, Closure]>(PlayableListSortFieldKey) const [sortField] = requireInjection<[Ref<MaybeArray<PlayableListSortField>>, Closure]>(PlayableListSortFieldKey)
const [config] = requireInjection<[Partial<PlayableListConfig>]>(PlayableListConfigKey, [{}]) const [config] = requireInjection<[Partial<PlayableListConfig>]>(PlayableListConfigKey, [{}])

View file

@ -4,7 +4,7 @@ import { ref } from 'vue'
import { expect, it } from 'vitest' import { expect, it } from 'vitest'
import UnitTestCase from '@/__tests__/UnitTestCase' import UnitTestCase from '@/__tests__/UnitTestCase'
import factory from '@/__tests__/factory' import factory from '@/__tests__/factory'
import { PlayablesKey, SelectedPlayablesKey } from '@/symbols' import { FilteredPlayablesKey, PlayablesKey, SelectedPlayablesKey } from '@/symbols'
import SongListControls from './SongListControls.vue' import SongListControls from './SongListControls.vue'
new class extends UnitTestCase { new class extends UnitTestCase {
@ -79,6 +79,7 @@ new class extends UnitTestCase {
global: { global: {
provide: { provide: {
[<symbol>PlayablesKey]: [ref(songs)], [<symbol>PlayablesKey]: [ref(songs)],
[<symbol>FilteredPlayablesKey]: [ref(songs)],
[<symbol>SelectedPlayablesKey]: [ref(take(songs, selectedSongCount))], [<symbol>SelectedPlayablesKey]: [ref(take(songs, selectedSongCount))],
}, },
}, },

View file

@ -4,7 +4,7 @@
<BtnGroup uppercase> <BtnGroup uppercase>
<template v-if="altPressed"> <template v-if="altPressed">
<Btn <Btn
v-if="selectedPlayables.length < 2 && playables.length" v-if="selectedPlayables.length < 2 && filteredPlayables.length"
v-koel-tooltip.bottom v-koel-tooltip.bottom
class="btn-play-all" class="btn-play-all"
highlight highlight
@ -30,7 +30,7 @@
<template v-else> <template v-else>
<Btn <Btn
v-if="selectedPlayables.length < 2 && playables.length" v-if="selectedPlayables.length < 2 && filteredPlayables.length"
v-koel-tooltip.bottom v-koel-tooltip.bottom
class="btn-shuffle-all" class="btn-shuffle-all"
data-testid="btn-shuffle-all" data-testid="btn-shuffle-all"
@ -85,7 +85,7 @@
</Btn> </Btn>
</BtnGroup> </BtnGroup>
<BtnGroup v-if="config.filter && playables.length"> <BtnGroup v-if="config.filter && allPlayables.length">
<SongListFilter @change="filter" /> <SongListFilter @change="filter" />
</BtnGroup> </BtnGroup>
</div> </div>
@ -103,7 +103,7 @@ import { faPlay, faRandom, faRotateRight, faTrashCan } from '@fortawesome/free-s
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, toRef, watch } from 'vue' import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, toRef, watch } from 'vue'
import { OnClickOutside } from '@vueuse/components' import { OnClickOutside } from '@vueuse/components'
import { PlayablesKey, SelectedPlayablesKey } from '@/symbols' import { FilteredPlayablesKey, PlayablesKey, SelectedPlayablesKey } from '@/symbols'
import { requireInjection } from '@/utils/helpers' import { requireInjection } from '@/utils/helpers'
import { useFloatingUi } from '@/composables/useFloatingUi' import { useFloatingUi } from '@/composables/useFloatingUi'
@ -123,7 +123,8 @@ const SongListFilter = defineAsyncComponent(() => import('@/components/song/song
const config = toRef(props, 'config') const config = toRef(props, 'config')
const [playables] = requireInjection<[Ref<Playable[]>]>(PlayablesKey) const [allPlayables] = requireInjection<[Ref<Playable[]>]>(PlayablesKey)
const [filteredPlayables] = requireInjection<[Ref<Playable[]>]>(FilteredPlayablesKey)
const [selectedPlayables] = requireInjection(SelectedPlayablesKey) const [selectedPlayables] = requireInjection(SelectedPlayablesKey)
const addToButton = ref<InstanceType<typeof Btn>>() const addToButton = ref<InstanceType<typeof Btn>>()

View file

@ -12,6 +12,7 @@ import { useFuzzySearch } from '@/composables/useFuzzySearch'
import { useRouter } from '@/composables/useRouter' import { useRouter } from '@/composables/useRouter'
import { import {
FilteredPlayablesKey,
PlayableListConfigKey, PlayableListConfigKey,
PlayableListContextKey, PlayableListContextKey,
PlayableListSortFieldKey, PlayableListSortFieldKey,
@ -40,7 +41,7 @@ export const useSongList = (
config = reactive(config) config = reactive(config)
context = reactive(context) context = reactive(context)
const { isCurrentScreen, go } = useRouter() const { isCurrentScreen, go, url } = useRouter()
const fuzzy = config.filterable const fuzzy = config.filterable
? useFuzzySearch(playables, [ ? useFuzzySearch(playables, [
@ -64,12 +65,15 @@ export const useSongList = (
if (!config.sortable) { if (!config.sortable) {
return null return null
} }
if (isCurrentScreen('Artist', 'Album')) { if (isCurrentScreen('Artist', 'Album')) {
return 'track' return 'track'
} }
if (isCurrentScreen('Search.Songs', 'Queue', 'RecentlyPlayed')) { if (isCurrentScreen('Search.Songs', 'Queue', 'RecentlyPlayed')) {
return null return null
} }
return 'title' return 'title'
})()) })())
@ -108,9 +112,11 @@ export const useSongList = (
if (!commonStore.state.allows_download) { if (!commonStore.state.allows_download) {
return false return false
} }
if (playables.value.length === 0) { if (playables.value.length === 0) {
return false return false
} }
return playables.value.length === 1 || commonStore.state.supports_batch_downloading return playables.value.length === 1 || commonStore.state.supports_batch_downloading
}) })
@ -127,7 +133,7 @@ export const useSongList = (
const playAll = (shuffle: boolean) => { const playAll = (shuffle: boolean) => {
playbackService.queueAndPlay(getPlayablesToPlay(), shuffle) playbackService.queueAndPlay(getPlayablesToPlay(), shuffle)
go('queue') go(url('queue'))
} }
const playSelected = (shuffle: boolean) => playbackService.queueAndPlay(selectedPlayables.value, shuffle) const playSelected = (shuffle: boolean) => playbackService.queueAndPlay(selectedPlayables.value, shuffle)
@ -185,7 +191,8 @@ export const useSongList = (
eventBus.on('SONGS_DELETED', deletedSongs => (playables.value = differenceBy(playables.value, deletedSongs, 'id'))) eventBus.on('SONGS_DELETED', deletedSongs => (playables.value = differenceBy(playables.value, deletedSongs, 'id')))
provideReadonly(PlayablesKey, filteredPlayables, false) provideReadonly(PlayablesKey, playables, false)
provideReadonly(FilteredPlayablesKey, filteredPlayables, false)
provideReadonly(SelectedPlayablesKey, selectedPlayables, false) provideReadonly(SelectedPlayablesKey, selectedPlayables, false)
provideReadonly(PlayableListConfigKey, config) provideReadonly(PlayableListConfigKey, config)
provideReadonly(PlayableListContextKey, context) provideReadonly(PlayableListContextKey, context)

View file

@ -11,7 +11,8 @@ export const OverlayKey: InjectionKey<Ref<InstanceType<typeof Overlay>>> = Symbo
export const DialogBoxKey: InjectionKey<Ref<InstanceType<typeof DialogBox>>> = Symbol('DialogBox') export const DialogBoxKey: InjectionKey<Ref<InstanceType<typeof DialogBox>>> = Symbol('DialogBox')
export const MessageToasterKey: InjectionKey<Ref<InstanceType<typeof MessageToaster>>> = Symbol('MessageToaster') export const MessageToasterKey: InjectionKey<Ref<InstanceType<typeof MessageToaster>>> = Symbol('MessageToaster')
export const PlayablesKey: ReadonlyInjectionKey<Ref<Playable[]>> | InjectionKey<Ref<Playable[]>> = Symbol('Playables') export const PlayablesKey: ReadonlyInjectionKey<Ref<Playable[]>> | InjectionKey<Ref<Playable[]>> = Symbol('PlayablesKey')
export const FilteredPlayablesKey: ReadonlyInjectionKey<Ref<Playable[]>> | InjectionKey<Ref<Playable[]>> = Symbol('FilteredPlayablesKey')
export const CurrentPlayableKey: InjectionKey<Ref<Playable | undefined>> = Symbol('CurrentPlayable') export const CurrentPlayableKey: InjectionKey<Ref<Playable | undefined>> = Symbol('CurrentPlayable')
export const SelectedPlayablesKey: ReadonlyInjectionKey<Ref<Playable[]>> = Symbol('SelectedPlayables') export const SelectedPlayablesKey: ReadonlyInjectionKey<Ref<Playable[]>> = Symbol('SelectedPlayables')
export const PlayableListConfigKey: ReadonlyInjectionKey<Partial<PlayableListConfig>> = Symbol('SongListConfig') export const PlayableListConfigKey: ReadonlyInjectionKey<Partial<PlayableListConfig>> = Symbol('SongListConfig')