ComposeWithMe

Delete Songs and Tracks

Type-to-confirm deletion for songs and individual tracks

Overview

Owners can delete a single track or an entire song. Both go through the same type-to-confirm modal — the user must type the literal word Delete before the action is enabled — rather than a plain "Are you sure?" dialog, to make an irreversible action harder to trigger by accident.

How it works

  • DeleteConfirmModal.tsx is generic: it takes a title, description, optional advisory text, and an onConfirm callback — used for both the track-delete and song-delete flows with different copy.
  • The confirm button stays disabled until the typed text exactly matches Delete (CONFIRM_WORD).
  • Track delete: DELETE /api/songs/[id]/tracks/[trackName] → deleteSongTrack() in src/lib/storage.ts, which branches per backend (GitHub file delete vs. Blob object delete).
  • Song delete: DELETE /api/songs/[id] checks song.ownerId === session.user.id (403 otherwise), calls deleteSongObjects(), then deletes the Song row from Postgres.
  • Delete buttons only render at all when the viewer is the owner (MultiTrackPlayer.tsx passes onDeleteClick conditionally), on top of the server-side ownership check.

Key files

FileRole
src/components/DeleteConfirmModal.tsxGeneric type-to-confirm modal
src/app/api/songs/[id]/route.tsDELETE — song deletion (ownership check, storage cleanup, Postgres delete)
src/app/api/songs/[id]/tracks/[trackName]/route.tsDELETE — single track deletion
src/lib/storage.tsdeleteSongTrack(), deleteSongObjects()

Limitations / notes

  • Deleting a GITHUB-backed song only removes its Postgres row — the GitHub repo itself is left behind (see Storage Backends for why: no delete_repo OAuth scope). Deleting a BLOB-backed song does remove every object under its prefix.
  • No undo/trash — deletion is immediate and final once confirmed.