fix: broken song reordering (#1524)

This commit is contained in:
Phan An 2022-10-10 09:00:02 +02:00 committed by GitHub
parent d7a361e532
commit 2edbda54d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 45 deletions

View file

@ -87,11 +87,11 @@
:item="item"
draggable="true"
@click="rowClicked(item, $event)"
@dragleave="removeDroppableState"
@dragstart="rowDragStart(item, $event)"
@dragenter.prevent="allowDrop"
@dragleave="onDragLeave"
@dragstart="onDragStart(item, $event)"
@dragenter.prevent="onDragEnter"
@dragover.prevent
@drop.prevent="handleDrop(item, $event)"
@drop.prevent="onDrop(item, $event)"
@contextmenu.prevent="openContextMenu(item, $event)"
/>
</VirtualScroller>
@ -103,9 +103,9 @@ import { findIndex } from 'lodash'
import isMobile from 'ismobilejs'
import { faAngleDown, faAngleUp } from '@fortawesome/free-solid-svg-icons'
import { faClock } from '@fortawesome/free-regular-svg-icons'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { computed, onMounted, Ref, ref, watch } from 'vue'
import { eventBus, requireInjection } from '@/utils'
import { useDraggable } from '@/composables'
import { useDraggable, useDroppable } from '@/composables'
import {
ScreenNameKey,
SelectedSongsKey,
@ -119,15 +119,16 @@ import VirtualScroller from '@/components/ui/VirtualScroller.vue'
import SongListItem from '@/components/song/SongListItem.vue'
const { startDragging } = useDraggable('songs')
const { getDroppedData, acceptsDrop } = useDroppable(['songs'])
const emit = defineEmits(['press:enter', 'press:delete', 'reorder', 'sort', 'scroll-breakpoint', 'scrolled-to-end'])
const [items] = requireInjection(SongsKey)
const [items] = requireInjection<[Ref<Song[]>]>(SongsKey)
const [screen] = requireInjection<[ScreenName]>(ScreenNameKey)
const [selectedSongs, setSelectedSongs] = requireInjection(SelectedSongsKey)
const [sortField, setSortField] = requireInjection(SongListSortFieldKey)
const [sortOrder, setSortOrder] = requireInjection(SongListSortOrderKey)
const [injectedConfig] = requireInjection(SongListConfigKey, {})
const [selectedSongs, setSelectedSongs] = requireInjection<[Ref<Song[]>, Closure]>(SelectedSongsKey)
const [sortField, setSortField] = requireInjection<[Ref<SongListSortField>, Closure]>(SongListSortFieldKey)
const [sortOrder, setSortOrder] = requireInjection<[Ref<SortOrder>, Closure]>(SongListSortOrderKey)
const [injectedConfig] = requireInjection<[Partial<SongListConfig>]>(SongListConfigKey, [{}])
const lastSelectedRow = ref<SongRow>()
const sortFields = ref<SongListSortField[]>([])
@ -250,12 +251,7 @@ const selectRowsBetween = (first: SongRow, second: SongRow) => {
}
}
/**
* Enable dragging songs by capturing the dragstart event on a table row.
* Even though the event is triggered on one row only, we'll collect other
* selected rows, if any, as well.
*/
const rowDragStart = (row: SongRow, event: DragEvent) => {
const onDragStart = (row: SongRow, event: DragEvent) => {
// If the user is dragging an unselected row, clear the current selection.
if (!row.selected) {
clearSelection()
@ -265,52 +261,42 @@ const rowDragStart = (row: SongRow, event: DragEvent) => {
startDragging(event, selectedSongs.value)
}
/**
* Add a "droppable" class and set the drop effect when other songs are dragged over a row.
*/
const allowDrop = (event: DragEvent) => {
if (!allowReordering) return;
const onDragEnter = (event: DragEvent) => {
if (!allowReordering) return
(event.target as Element).parentElement?.classList.add('droppable')
event.dataTransfer!.dropEffect = 'move'
if (acceptsDrop(event)) {
(event.target as Element).parentElement?.classList.add('droppable')
event.dataTransfer!.dropEffect = 'move'
}
return false
}
const handleDrop = (item: SongRow, event: DragEvent) => {
if (
!allowReordering ||
!event.dataTransfer!.getData('application/x-koel.text+plain') ||
!selectedSongs.value.length
) {
return removeDroppableState(event)
const onDrop = (item: SongRow, event: DragEvent) => {
if (!allowReordering || !getDroppedData(event) || !selectedSongs.value.length) {
return onDragLeave(event)
}
emit('reorder', item.song)
return removeDroppableState(event)
return onDragLeave(event)
}
const removeDroppableState = (event: DragEvent) => {
const onDragLeave = (event: DragEvent) => {
(event.target as Element).parentElement?.classList.remove('droppable')
return false
}
const openContextMenu = async (row: SongRow, event: MouseEvent) => {
// If the user is right-clicking an unselected row,
// clear the current selection and select it instead.
if (!row.selected) {
clearSelection()
toggleRow(row)
}
await nextTick()
eventBus.emit('SONG_CONTEXT_MENU_REQUESTED', event, selectedSongs.value)
}
defineExpose({
getAllSongsWithSort,
sort
getAllSongsWithSort
})
onMounted(() => render())

View file

@ -2,7 +2,8 @@ import { arrayify, logger, pluralize } from '@/utils'
import { albumStore, artistStore, playlistStore, songStore } from '@/stores'
type Draggable = Song | Song[] | Album | Artist | Playlist
type DraggableType = 'songs' | 'album' | 'artist' | 'playlist'
const draggableTypes = <const>['songs', 'album', 'artist', 'playlist']
type DraggableType = typeof draggableTypes[number]
const createGhostDragImage = (event: DragEvent, text: string): void => {
if (!event.dataTransfer) {
@ -23,11 +24,7 @@ const createGhostDragImage = (event: DragEvent, text: string): void => {
}
const getDragType = (event: DragEvent) => {
const types: DraggableType[] = ['songs', 'album', 'artist', 'playlist']
for (let i = 0, count = types.length; i < count; ++i) {
if (event.dataTransfer?.types.includes(`application/x-koel.${types[i]}`)) return types[i]
}
return draggableTypes.find(type => event.dataTransfer?.types.includes(`application/x-koel.${type}`))
}
export const useDraggable = (type: DraggableType) => {
@ -139,6 +136,7 @@ export const useDroppable = (acceptedTypes: DraggableType[]) => {
return {
acceptsDrop,
getDroppedData,
resolveDroppedValue,
resolveDroppedSongs
}