Skip to content

feat(collection): move add-by-URL into SuggestedTracks panel; remove confusing IconLink#14429

Merged
dylanjeffers merged 2 commits into
mainfrom
feat/add-by-url-paste-in-suggested-tracks
May 30, 2026
Merged

feat(collection): move add-by-URL into SuggestedTracks panel; remove confusing IconLink#14429
dylanjeffers merged 2 commits into
mainfrom
feat/add-by-url-paste-in-suggested-tracks

Conversation

@dylanjeffers
Copy link
Copy Markdown
Contributor

Per Ray's Slack feedback + Julian's UX suggestion: the copy-link-looking IconButton on the playlist owner header was confusing — its actual behavior (open a "paste track URLs" modal) wasn't discoverable from the icon, and adding tracks by URL belongs in the same section users already think of as "where I add tracks."

The natural action for "add by URL" is to paste — no separate modal or icon required. This PR moves the feature inline into the SuggestedTracks panel (already titled "Add some tracks", already the obvious place to add) as an always-visible TextInput that paste-detects, and removes the previous trigger + modal entirely.

Before / After

Before: Owner header had an IconLink button next to Edit/Share/Publish/Overflow → opened a separate AddTracksByUrlModal with a multi-line textarea.

After: Owner header is lean (Edit / Share / Publish / Overflow). The SuggestedTracks panel — already shown for playlist owners — gets an always-visible single-line input row between the "Add some tracks" heading and the (collapsible) suggested-track list. Paste a URL → it's added.

What the input does

  • Paste detection. onPaste reads the clipboard text via e.clipboardData.getData('text'). If parseTrackUrls finds at least one valid Audius track URL (same parser the modal used, via getPathFromTrackUrl), it preventDefaults and runs the add flow. If the clipboard isn't a track URL the paste falls through normally — the field behaves like a regular text input.
  • Enter to commit. If the user types or partial-pastes and presses Enter, we run the same parse + add flow on the current input value.
  • Multi-URL still works. Pasting multiple URLs separated by newlines / commas / tabs adds them all (same as the old modal).

The add flow (unchanged from the old modal)

  1. Parse permalinks from input (getPathFromTrackUrl, dedupe).
  2. Capacity check vs PLAYLIST_TRACK_LIMIT (100) using the existing playlist's track IDs.
  3. Resolve via sdk.tracks.getBulkTracks({ permalink, userId }).
  4. Skip duplicates already in the playlist; skip those over remaining capacity.
  5. Dispatch addTrackToPlaylist per track, spaced by INTER_DISPATCH_DELAY_MS so each saga's optimistic update lands before the next one reads playlist state.
  6. Draft branch: if the collection is unsaved (isDraftCollection), route through addTrackToDraftCollection instead — same conditional the suggested-row Add button already uses. This is one improvement on the old modal, which didn't route to drafts.
  7. Toast summary: Added N tracks • M already in playlist • K not found • J invalid links • L skipped (playlist limit reached).

Files changed

File Change
packages/web/src/components/suggested-tracks/SuggestedTracks.tsx +TextInput row, +addTracksFromText (logic mirrored from the deleted modal), +draft routing for URL adds
packages/web/src/components/collection/desktop/OwnerActionButtons.tsx Drop the IconLink IconButton + modal-hook usage + unused messages
DELETED: packages/web/src/components/add-tracks-by-url-modal/AddTracksByUrlModal.tsx + dir The whole modal component
DELETED: packages/common/src/store/ui/modals/add-tracks-by-url-modal/index.ts + dir The store slice
packages/common/src/store/ui/modals/{types,parentSlice,reducers,index}.ts Drop the AddTracksByUrlModal registrations
packages/web/src/pages/modals/Modals.tsx Drop the import + entry from commonModalsMap

Net diff: +272 / −355.

Surface area preserved

The old IconLink button only showed when !is_album && !ddex_app. The SuggestedTracks panel is already gated on isOwner && !is_album && !ddex_app — same surface — so feature availability is unchanged. Albums and DDEX-imported collections continue not to have URL-add (separate decision, out of scope here).

Verification

  • tsc --noEmit clean in packages/common
  • tsc --noEmit clean in packages/web
  • Manual: open a playlist you own, scroll to the "Add some tracks" panel, paste a track URL into the input — confirm it adds and toasts the summary.
  • Manual: paste 5 URLs separated by newlines — confirm all are added (or appropriately deduped / capped).
  • Manual: type some non-URL text and press Enter — confirm "No valid Audius track links found." toast.
  • Manual: paste a URL into the input while playlist has 99 tracks — confirm capacity guard toast.
  • Manual: in the inline create-playlist flow (draft), paste a URL — confirm it adds to the draft (via addTrackToDraftCollection).
  • Manual: verify the IconLink button is gone from the owner header on a playlist page.

Couldn't run a local web preview to smoke-test (audius web needs the full protocol stack via Docker per CLAUDE.md). Flagging for manual review.

Follow-ups (deliberately out of scope)

  • The input acts as a paste/Enter receiver, not a search field — typing non-URL text doesn't search anything. If product wants real type-to-search-tracks (Spotify-style), that's a separate feature.
  • Mobile equivalent: this PR is web only (per the task spec). The mobile collection-edit flow doesn't currently have an analogous "add tracks by URL" feature; if we want one, a follow-up PR can port this pattern to the mobile CollectionScreen.

🤖 Generated with Claude Code

…confusing IconLink

Ray flagged the copy-link-looking IconButton on the playlist owner header
as confusing — its actual behavior (open a "paste track URLs" modal) wasn't
discoverable from the icon, and adding tracks by URL belongs in the same
section users already think of as "where I add tracks."

Per Julian's UX suggestion, the natural action for "add by URL" is to
paste — no separate modal or icon required. Moves the feature inline into
the SuggestedTracks panel (already titled "Add some tracks", already the
obvious place to add) as an always-visible TextInput that paste-detects.

Changes:
- packages/web/src/components/suggested-tracks/SuggestedTracks.tsx: add an
  always-visible TextInput row between the heading and the collapsible
  suggestion list. onPaste: if the clipboard contains at least one valid
  Audius track URL (parsed via getPathFromTrackUrl), prevent default,
  resolve via sdk.tracks.getBulkTracks, run the same dedupe + capacity +
  delay-spaced dispatch the modal did, toast a summary. onKeyDown(Enter)
  handles a typed URL too. If the pasted text isn't a URL the input
  behaves like a normal text field — search-field UX. Routes adds through
  draftCollectionCache when the collection is unsaved (mirrors the
  suggested-row Add button's draft branch).
- packages/web/src/components/collection/desktop/OwnerActionButtons.tsx:
  drop the IconLink IconButton and its modal-opener handler / messages /
  hook usage. Header is now lean (Edit, Share, Publish, OverflowMenu).
- Delete the now-unused AddTracksByUrlModal entirely: the React component
  (packages/web/src/components/add-tracks-by-url-modal/), the common
  store slice (packages/common/src/store/ui/modals/add-tracks-by-url-modal/),
  and its registrations in parentSlice, reducers, types, the modals
  re-export, and Modals.tsx. No other call sites.

Feature parity preserved:
- Same parser (one URL per line, or comma/tab separated) — multi-URL paste
  still works.
- Same dedupe vs existing playlist contents.
- Same PLAYLIST_TRACK_LIMIT (100) capacity check.
- Same getBulkTracks SDK call + userTrackMetadataFromSDK transform.
- Same summary toast format (added • duplicates • not found • invalid •
  over-limit).
- Same playlist-only / non-DDEX surface (SuggestedTracks already gated on
  isOwner && !isAlbum && playlist, mirrors the old IconLink condition).

Verification:
- tsc --noEmit clean in packages/common and packages/web.
- Visual smoke needs the full protocol stack (audius web requires Docker
  per CLAUDE.md) — flagging on the PR for manual review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 30, 2026

⚠️ No Changeset found

Latest commit: 95d32b5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

CI lint surfaced 5 errors on the prior commit — 3 prettier formatting and
2 no-void. Auto-fixed prettier; replaced `void addTracksFromText(...)`
with a bare call (the function already catches its own errors and toasts;
the floating promise is intentional and now documented).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🌐 Web preview ready

Preview URL: https://audius-web-preview-pr-14429.audius.workers.dev

Unique preview for this PR (deployed from this branch).
Workflow run

@dylanjeffers dylanjeffers merged commit fae9c59 into main May 30, 2026
22 of 23 checks passed
@dylanjeffers dylanjeffers deleted the feat/add-by-url-paste-in-suggested-tracks branch May 30, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant