Skip to content

Commit 26e0fd9

Browse files
fix(playlist): allow removing the last track from a playlist (#14306)
## Summary - **UI:** The trash icon in `CollectionTrackFieldArray` was disabled whenever only one track remained. That guard fits the upload flow (which reuses the same form) but blocked edits to existing playlists from ever emptying out. `isUpload` now flows through from `EditCollectionForm`, so the guard only applies during upload. - **Backend:** Removing the final track via the kebab menu's "Remove from playlist" appeared to succeed (optimistic update + toast) but the track reappeared on reload. In `populate_playlist_record_metadata`, a Python truthiness check on `playlist_metadata["playlist_contents"]` treated the SDK's empty-array payload (`[]`) as if the field were omitted and silently skipped the update. Switched to `is None` so an explicit empty list is applied. Downstream `update_playlist_tracks` already handles an empty `track_ids` correctly (marks all existing rows removed). ## Repro 1. Visit a playlist with one track. 2. Track kebab → "Remove from playlist". 3. Confirmer succeeds, toast shows, track disappears from view. 4. Reload — track is still there. Also: open the same playlist via "Edit Playlist" — the trash icon on the only row was disabled. ## Test plan - [ ] Edit-playlist form: remove the final track and save → playlist is empty on reload. - [ ] Track kebab → "Remove from playlist" on a one-track playlist → playlist is empty on reload. - [ ] Regression: removing one of several tracks still works in both paths. - [ ] Regression: upload flow still prevents bottoming out at zero tracks. - [ ] Backend regression: `playlist_contents: {"track_ids": []}` (legacy dict form, used in existing tests) continues to update correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent efa8352 commit 26e0fd9

3 files changed

Lines changed: 13 additions & 4 deletions

File tree

packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,10 @@ def populate_playlist_record_metadata(
677677
# Update the playlist_record when the corresponding field exists
678678
# in playlist_metadata
679679
if key == "playlist_contents":
680-
if not playlist_metadata.get(key):
680+
# Use `is None` rather than a truthiness check so that an explicit
681+
# empty list (the user removing the last track) is applied instead
682+
# of being silently treated as "field omitted".
683+
if playlist_metadata.get(key) is None:
681684
continue
682685
playlist_record.playlist_contents = process_playlist_contents(
683686
playlist_record,

packages/web/src/components/edit-collection/EditCollectionForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export const EditCollectionForm = (props: EditCollectionFormProps) => {
195195
</Flex>
196196
) : null}
197197
</Tile>
198-
<CollectionTrackFieldArray />
198+
<CollectionTrackFieldArray isUpload={isUpload} />
199199
{isUpload ? <AnchoredSubmitRow /> : <AnchoredSubmitRowEdit />}
200200
{playlist_id ? (
201201
<>

packages/web/src/components/edit/fields/CollectionTrackFieldArray.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ const makeTrackKey = (track: TrackForUpload | TrackForEdit, index: number) => {
6666
return `${track.metadata.track_id}${suffix}`
6767
}
6868

69-
export const CollectionTrackFieldArray = () => {
69+
type CollectionTrackFieldArrayProps = {
70+
isUpload?: boolean
71+
}
72+
73+
export const CollectionTrackFieldArray = ({
74+
isUpload = false
75+
}: CollectionTrackFieldArrayProps = {}) => {
7076
const [{ value: tracks }] =
7177
useField<(TrackForUpload | TrackForEdit)[]>('tracks')
7278

@@ -104,7 +110,7 @@ export const CollectionTrackFieldArray = () => {
104110
<CollectionTrackField
105111
index={index}
106112
remove={remove}
107-
disableDelete={tracks.length === 1}
113+
disableDelete={isUpload && tracks.length === 1}
108114
/>
109115
</div>
110116
)}

0 commit comments

Comments
 (0)