enable drag and drop reordering
This commit is contained in:
+23
-83
@@ -1,9 +1,6 @@
|
||||
package sharedutil
|
||||
|
||||
import (
|
||||
"math"
|
||||
"slices"
|
||||
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
@@ -106,90 +103,33 @@ func TracksToIDs(tracks []*mediaprovider.Track) []string {
|
||||
})
|
||||
}
|
||||
|
||||
type TrackReorderOp int
|
||||
|
||||
const (
|
||||
MoveToTop TrackReorderOp = iota
|
||||
MoveToBottom
|
||||
MoveUp
|
||||
MoveDown
|
||||
)
|
||||
|
||||
// Reorder items and return a new track slice.
|
||||
// idxToMove must contain only valid indexes into tracks, and no repeats
|
||||
func ReorderItems[T any](items []T, idxToMove []int, op TrackReorderOp) []T {
|
||||
newItems := make([]T, len(items))
|
||||
switch op {
|
||||
case MoveToTop:
|
||||
topIdx := 0
|
||||
botIdx := len(idxToMove)
|
||||
idxToMoveSet := ToSet(idxToMove)
|
||||
for i, t := range items {
|
||||
if _, ok := idxToMoveSet[i]; ok {
|
||||
newItems[topIdx] = t
|
||||
topIdx++
|
||||
} else {
|
||||
newItems[botIdx] = t
|
||||
botIdx++
|
||||
}
|
||||
func ReorderItems[T any](items []T, idxToMove []int, insertIdx int) []T {
|
||||
idxToMoveSet := ToSet(idxToMove)
|
||||
|
||||
newItems := make([]T, 0, len(items))
|
||||
|
||||
// collect items that will end up before the insertion set
|
||||
i := 0
|
||||
for ; i < len(items); i++ {
|
||||
if insertIdx == i {
|
||||
break
|
||||
}
|
||||
case MoveToBottom:
|
||||
topIdx := 0
|
||||
botIdx := len(items) - len(idxToMove)
|
||||
idxToMoveSet := ToSet(idxToMove)
|
||||
for i, t := range items {
|
||||
if _, ok := idxToMoveSet[i]; ok {
|
||||
newItems[botIdx] = t
|
||||
botIdx++
|
||||
} else {
|
||||
newItems[topIdx] = t
|
||||
topIdx++
|
||||
}
|
||||
}
|
||||
case MoveUp:
|
||||
first := firstIdxCanMoveUp(idxToMove)
|
||||
copy(newItems, items)
|
||||
for _, i := range idxToMove {
|
||||
if i < first {
|
||||
continue
|
||||
}
|
||||
newItems[i-1], newItems[i] = newItems[i], newItems[i-1]
|
||||
}
|
||||
case MoveDown:
|
||||
last := lastIdxCanMoveDown(idxToMove, len(items))
|
||||
copy(newItems, items)
|
||||
for i := len(idxToMove) - 1; i >= 0; i-- {
|
||||
idx := idxToMove[i]
|
||||
if idx > last {
|
||||
continue
|
||||
}
|
||||
newItems[idx+1], newItems[idx] = newItems[idx], newItems[idx+1]
|
||||
if _, ok := idxToMoveSet[i]; !ok {
|
||||
newItems = append(newItems, items[i])
|
||||
}
|
||||
}
|
||||
|
||||
for _, idx := range idxToMove {
|
||||
newItems = append(newItems, items[idx])
|
||||
}
|
||||
|
||||
for ; i < len(items); i++ {
|
||||
if _, ok := idxToMoveSet[i]; !ok {
|
||||
newItems = append(newItems, items[i])
|
||||
}
|
||||
}
|
||||
|
||||
return newItems
|
||||
}
|
||||
|
||||
func firstIdxCanMoveUp(idxs []int) int {
|
||||
prevIdx := -1
|
||||
slices.Sort(idxs)
|
||||
for _, idx := range idxs {
|
||||
if idx > prevIdx+1 {
|
||||
return idx
|
||||
}
|
||||
prevIdx = idx
|
||||
}
|
||||
return math.MaxInt
|
||||
}
|
||||
|
||||
func lastIdxCanMoveDown(idxs []int, lenSlice int) int {
|
||||
prevIdx := lenSlice
|
||||
slices.Sort(idxs)
|
||||
for i := len(idxs) - 1; i >= 0; i-- {
|
||||
idx := idxs[i]
|
||||
if idx < prevIdx-1 {
|
||||
return idx
|
||||
}
|
||||
prevIdx = idx
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_ReorderItems(t *testing.T) {
|
||||
|
||||
tracks := []*mediaprovider.Track{
|
||||
{ID: "a"}, // 0
|
||||
{ID: "b"}, // 1
|
||||
@@ -27,7 +28,7 @@ func Test_ReorderItems(t *testing.T) {
|
||||
{ID: "b"},
|
||||
{ID: "e"},
|
||||
}
|
||||
newTracks := ReorderItems(tracks, idxToMove, MoveToTop)
|
||||
newTracks := ReorderItems(tracks, idxToMove, 0)
|
||||
if !tracklistsEqual(t, newTracks, want) {
|
||||
t.Error("ReorderTracks: MoveToTop order incorrect")
|
||||
}
|
||||
@@ -42,40 +43,10 @@ func Test_ReorderItems(t *testing.T) {
|
||||
{ID: "c"},
|
||||
{ID: "f"},
|
||||
}
|
||||
newTracks = ReorderItems(tracks, idxToMove, MoveToBottom)
|
||||
newTracks = ReorderItems(tracks, idxToMove, len(tracks))
|
||||
if !tracklistsEqual(t, newTracks, want) {
|
||||
t.Error("ReorderTracks: MoveToBottom order incorrect")
|
||||
}
|
||||
|
||||
// test MoveUp:
|
||||
idxToMove = []int{0, 1, 3, 5}
|
||||
want = []*mediaprovider.Track{
|
||||
{ID: "a"},
|
||||
{ID: "b"},
|
||||
{ID: "d"},
|
||||
{ID: "c"},
|
||||
{ID: "f"},
|
||||
{ID: "e"},
|
||||
}
|
||||
newTracks = ReorderItems(tracks, idxToMove, MoveUp)
|
||||
if !tracklistsEqual(t, newTracks, want) {
|
||||
t.Error("ReorderTracks: MoveUp order incorrect")
|
||||
}
|
||||
|
||||
// test MoveDown:
|
||||
idxToMove = []int{2, 4, 5}
|
||||
want = []*mediaprovider.Track{
|
||||
{ID: "a"},
|
||||
{ID: "b"},
|
||||
{ID: "d"},
|
||||
{ID: "c"},
|
||||
{ID: "e"},
|
||||
{ID: "f"},
|
||||
}
|
||||
newTracks = ReorderItems(tracks, idxToMove, MoveDown)
|
||||
if !tracklistsEqual(t, newTracks, want) {
|
||||
t.Error("ReorderTracks: MoveDown order incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func tracklistsEqual(t *testing.T, a, b []*mediaprovider.Track) bool {
|
||||
|
||||
Reference in New Issue
Block a user