feat(tracks): remove from library behind a confirmation
The context menu's one destructive command. Its impact line says the files are not deleted, because a user who reads "remove" as "delete" and finds their music gone was failed by the copy rather than by the operation. The store patches rather than invalidates: the event carries the paths, so the tracks array — the expensive collection — is spliced in place and only the album/artist/genre summaries, whose counts really did change, are refetched. It falls back to a full invalidate when a tracks fetch is already in flight, which is the one case a patch cannot be shown to be equivalent to. Deleting an audio_files row cascades to queue_tracks, so the removal also compacts the queue — the same reload RemoveLibrary does, which unloads the player if the removed track was the one playing.
This commit is contained in:
@@ -129,6 +129,15 @@ func (l *Library) RemoveFromLibrary(filePaths []string) (*RemovalResult, error)
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting an audio_files row cascades to queue_tracks, so the
|
||||
// queue's in-memory copy now holds tracks the database does not —
|
||||
// including, possibly, the one playing. This is the same reload
|
||||
// RemoveLibrary does, and it unloads the player if the current
|
||||
// track was among them.
|
||||
if l.removalHooks.CompactQueue != nil {
|
||||
l.removalHooks.CompactQueue()
|
||||
}
|
||||
|
||||
// An album, artist or genre whose last track just went is now a row
|
||||
// with nothing behind it, and the album list selects from
|
||||
// release_groups rather than from audio_files — so it would keep
|
||||
|
||||
@@ -257,6 +257,32 @@ func TestRemoveFromLibrary_EmitsPatchablePayload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveFromLibrary_CompactsTheQueue pins the half that is invisible
|
||||
// from the track list: deleting an audio_files row cascades to
|
||||
// queue_tracks, so the queue's in-memory copy — and the player, if it
|
||||
// was the track playing — has to be told.
|
||||
func TestRemoveFromLibrary_CompactsTheQueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lib, dir, paths, _, libID := setupScanLibrary(t, 2)
|
||||
|
||||
lib.scanInternal(libID, "Test", dir)
|
||||
|
||||
compacted := 0
|
||||
|
||||
lib.SetRemovalHooks(RemovalHooks{
|
||||
CompactQueue: func() { compacted++ },
|
||||
})
|
||||
|
||||
if _, err := lib.RemoveFromLibrary([]string{paths[0]}); err != nil {
|
||||
t.Fatalf("RemoveFromLibrary: %v", err)
|
||||
}
|
||||
|
||||
if compacted != 1 {
|
||||
t.Errorf("CompactQueue called %d times, want 1", compacted)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveFromLibrary_RejectsAnEmptyRequest keeps a stray Delete on
|
||||
// an empty selection from reaching the database at all.
|
||||
func TestRemoveFromLibrary_RejectsAnEmptyRequest(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user