Skip to content

feat: sort playlist tracks by title, artist, or duration - #369

Closed
tsibog wants to merge 3 commits into
sozercan:mainfrom
tsibog:pr/playlist-sort
Closed

feat: sort playlist tracks by title, artist, or duration#369
tsibog wants to merge 3 commits into
sozercan:mainfrom
tsibog:pr/playlist-sort

Conversation

@tsibog

@tsibog tsibog commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a sort control to the playlist detail page. Tracks can be sorted by Title, Artist, or Duration (selecting the active option again toggles ascending/descending), with Custom order (the playlist's own order) as the default. The sorted order drives everything consistently: the visible list, row taps, and the header Play / Play Next / Add to Queue actions. Sorting is session-only view state — it never mutates the playlist.

"Date added" was considered and dropped: the browse response for VL<playlistId> carries no per-track added date and the API exposes no server-side sort for playlist contents (only library-level listings support an order param).

AI Prompt (Optional)

🤖 AI Prompt Used
I want to add sorting to the playlist detail view: by artist name, title,
date added (if possible) and duration. Look around in this repo for
guidelines and various docs.

AI Tool: Claude Code

Type of Change

  • ✨ New feature (non-breaking change that adds functionality)

Related Issues

None found.

Changes Made

  • PlaylistTrackSortOption + sortOption/sortAscending/displayedTracks/selectSort(_:) on PlaylistDetailViewModel — all sorting logic lives behind this small surface (localized standard compare, artist ties broken by title, nil durations always last, direction toggle on re-select)
  • PlaylistDetailView+TrackSort.swift — compact trailing sort Menu above the track list (checkmark + direction chevron on the active option), hidden for playlists with fewer than 2 tracks
  • PlaylistDetailView / +HeaderActions consume displayedTracks so display and playback order always match; the deferred full-playlist load now diffs queued tracks by videoId instead of a fixed count so a re-sort during pagination can't drop or duplicate tracks

Testing

  • Unit tests pass (swift test --skip KasetUITests)
  • Manual testing performed — sorted playlists by each option in both directions, played from sorted views, verified header actions follow the sorted order and Custom restores the original order
  • UI tested on macOS 26+

Checklist

  • My code follows the project's style guidelines
  • I have run swiftlint --strict && swiftformat .
  • I have added tests that prove my fix/feature works (5 new PlaylistDetailViewModelTests covering each option, direction toggle, artist tiebreak, nil-duration ordering)
  • New and existing unit tests pass locally
  • I have updated documentation if needed
  • I have checked for any performance implications
  • My changes generate no new warnings

Screenshots

Additional Notes

Includes the same test: deflake commit as #363/#364 (the NotificationServiceTests fixed-wait flake lives on main and hits fresh branches); it disappears on rebase once any of them merges.

Agent Transcript

Redacted agent session transcript (scoped to this PR)
source: local Claude Code session, trimmed to this PR's scope
omitted: raw tool outputs, system prompts, local paths, unrelated session turns

[user]
so i want to add sorting here [screenshot of playlist detail], by artist name,
title, date added (if possible) and duration. look around in this repo for
guidelines and various docs.

[assistant]
Scouted the repo first. Findings: the playlist page is PlaylistDetailView +
PlaylistDetailViewModel (@Observable), tracks are [Song] with title/artists/
duration all available client-side. No "date added" exists anywhere in the
parsed API data. No sort UI precedent in the app. The play queue is built from
the displayed array, so a sorted view plays in sorted order for free; row
removal keys off setVideoId, not index, so it's safe under re-sorting.

[user]
(decision) Investigate the API before dropping "date added"; sort state should
be session-only, not persisted.

[assistant]
Investigated: no per-track added date in the browse response for VL<playlistId>,
and no server-side sort params exist for playlist contents (only library-level
listings have an `order` param; cross-checked against ytmusicapi's parsers and
API surface). Verdict: date added is not feasible — shipping Custom order /
Title / Artist / Duration.

Implementation: PlaylistTrackSortOption + selectSort()/displayedTracks on the
view model (localizedStandardCompare, artist ties broken by title, nil
durations last, re-select toggles direction); compact sort Menu above the
track list; all consumers (rows, Play, Play Next, Add to Queue) switch to
displayedTracks. The deferred full-playlist load now diffs by videoId instead
of a fixed count so a re-sort during pagination can't drop or duplicate tracks.

[assistant]
Verification: swiftlint --strict clean, swiftformat clean, swift build OK,
swift test --skip KasetUITests → 1779/1779 pass incl. 5 new
PlaylistDetailViewModelTests (each option, direction toggle, artist tiebreak,
nil-duration ordering, reset-to-ascending). A separate guideline+architecture
review pass confirmed compliance (no print/force-unwraps, Swift Testing,
localization) and that the tests exercise only the view model's public surface.

[user]
Sorting works.

@tsibog
tsibog force-pushed the pr/playlist-sort branch from b5aaa2c to 17b727e Compare July 11, 2026 09:14
@tsibog
tsibog force-pushed the pr/playlist-sort branch from 17b727e to 4dae7d8 Compare July 11, 2026 09:19
- Move the sort control into the header action row, right-aligned after the
  Play/Queue buttons, instead of a separate row above the track list.
- Rename the 'Custom order' option to 'Default'.
- Give each track row a stable identity (song id + occurrence, numbered in
  playlist order to keep duplicates distinct) so re-sorting moves rows in place
  instead of swapping slot contents — the latter reloaded row artwork and
  like/dislike state, causing the blink and lag on sort changes.
- displayedTracks now derives from the identified rows so playback order stays
  in sync with the displayed order.
@tsibog

tsibog commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of #375. The sort feature works, but placing the sort control in the header action row causes scroll/hover jank (ViewThatFits re-measuring under the ScrollView). Handing the placement/design decision to a maintainer — details, root-cause bisect, and options in #375. Branch pr/playlist-sort retains the implementation + tests for reference.

@tsibog tsibog closed this Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant