Skip to content

feat(upl): sort the Ultimate Player channel list by name or channel n… - #62

Merged
iFlip721 merged 1 commit into
mainfrom
iss-51
Sep 3, 2026
Merged

feat(upl): sort the Ultimate Player channel list by name or channel n…#62
iFlip721 merged 1 commit into
mainfrom
iss-51

Conversation

@iFlip721

@iFlip721 iFlip721 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

…umber

Closes #51.

The rail's order came from a single computed (useUplData.orderedChannels) that always sorted by channel number, with the name only as a tiebreak. That explains every symptom in the issue thread:

  • "all the playlists EXCEPT localnow have been alphabetized" — most built-in sources never populate channelNo, so every row tied on the number key and fell through to the name tiebreak. The rail looked alphabetical by accident.
  • "it's not sorting the custom playlist I added" — a clone inherits each channel's number from its original source (00s Replay = 600000 sitting next to 6065), so the numeric key dominated and the list looked shuffled.

Adds an A-Z / # toggle to the channel rail header, defaulting to NAME order. The choice is a per-device viewer preference persisted under 'upl:sort', following UplVideoJsPlayer's existing 'upl:audio' pattern — same namespace, same defensive read, same silent fallback when storage is blocked. Deliberately not a Settings field: this bundle never fetches /api/settings, and importing useSettings would drag data.ts into the popup.

The channelNo comparator is the one the Playlist detail table already uses (numeric when both sides parse, else lexical, unnumbered last), which fixes two latent bugs in the old expression — both of which put unnumbered channels FIRST, directly against the comment that sat above it:

  • Number(null) is 0, not NaN, so a null channelNo sorted ahead of channel 1.
  • '' is not null, so a blank channelNo lexically outranked every real number.

Blank now counts as unnumbered in both the sort and the display ('—', matching a null one, instead of leaving a gap in the column). parseFloat rather than Number is load-bearing: it reads the leading number, so a '12A' subchannel sorts next to 12 instead of being exiled with the unparseable values.

A reorder moves every list index, so the rail re-pins its keyboard cursor to the playing channel on a sort change — as a watcher separate from the existing re-measure, which also fires on every filter keystroke, where yanking the cursor back would fight the person typing.

No server change. GET /api/playlists/:id/channels keeps its index-backed .sort({ group: 1, tvg_name: 1 }), which m3u/compose.ts deliberately mirrors for the .m3u export; the player re-sorts client-side instead.

Verified: npm run build (vue-tsc + vite) passes. Both orders, the persistence, the cursor re-pin, the filter-does-not-move-the-cursor guard, the 340px header layout and both themes were exercised in-browser against the real components, using the issue's own channel numbers.

…umber

Closes #51.

The rail's order came from a single computed (useUplData.orderedChannels) that
always sorted by channel number, with the name only as a tiebreak. That explains
every symptom in the issue thread:

  - "all the playlists EXCEPT localnow have been alphabetized" — most built-in
    sources never populate channelNo, so every row tied on the number key and
    fell through to the name tiebreak. The rail looked alphabetical by accident.
  - "it's not sorting the custom playlist I added" — a clone inherits each
    channel's number from its original source (00s Replay = 600000 sitting next
    to 6065), so the numeric key dominated and the list looked shuffled.

Adds an A-Z / # toggle to the channel rail header, defaulting to NAME order.
The choice is a per-device viewer preference persisted under 'upl:sort',
following UplVideoJsPlayer's existing 'upl:audio' pattern — same namespace,
same defensive read, same silent fallback when storage is blocked. Deliberately
not a Settings field: this bundle never fetches /api/settings, and importing
useSettings would drag data.ts into the popup.

The channelNo comparator is the one the Playlist detail table already uses
(numeric when both sides parse, else lexical, unnumbered last), which fixes two
latent bugs in the old expression — both of which put unnumbered channels FIRST,
directly against the comment that sat above it:

  - Number(null) is 0, not NaN, so a null channelNo sorted ahead of channel 1.
  - '' is not null, so a blank channelNo lexically outranked every real number.

Blank now counts as unnumbered in both the sort and the display ('—', matching
a null one, instead of leaving a gap in the column). parseFloat rather than
Number is load-bearing: it reads the leading number, so a '12A' subchannel sorts
next to 12 instead of being exiled with the unparseable values.

A reorder moves every list index, so the rail re-pins its keyboard cursor to the
playing channel on a sort change — as a watcher separate from the existing
re-measure, which also fires on every filter keystroke, where yanking the cursor
back would fight the person typing.

No server change. GET /api/playlists/:id/channels keeps its index-backed
.sort({ group: 1, tvg_name: 1 }), which m3u/compose.ts deliberately mirrors for
the .m3u export; the player re-sorts client-side instead.

Verified: `npm run build` (vue-tsc + vite) passes. Both orders, the persistence,
the cursor re-pin, the filter-does-not-move-the-cursor guard, the 340px header
layout and both themes were exercised in-browser against the real components,
using the issue's own channel numbers.
@iFlip721
iFlip721 merged commit 06c9465 into main Sep 3, 2026
6 checks passed
@iFlip721
iFlip721 deleted the iss-51 branch September 3, 2026 11:21
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.

Request - Ultimate video player - Sort Channels

1 participant