From 5c3568f7587d01432a659551c12bf1643af1804d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deluan=20Quint=C3=A3o?= Date: Mon, 5 Jan 2026 19:05:11 -0500 Subject: [PATCH] fix(ui): make playlist name sorting case-insensitive (#4845) * fix: make playlist name sorting case-insensitive Add collation NOCASE to playlist.name column to ensure case-insensitive sorting, matching the behavior of other tables like radio and user. This fixes the issue where uppercase playlist names would appear before lowercase names regardless of alphabetical order. The migration recreates the playlist table with the proper collation and recreates all associated indexes. Corresponding collation tests are added to verify the fix persists through future migrations. * fix: add default sorting to playlist names Signed-off-by: Deluan --------- Signed-off-by: Deluan --- ...4203627_playlist_case_insensitive_name.sql | 99 +++++++++++++++++++ persistence/collation_test.go | 2 + ui/src/playlist/PlaylistList.jsx | 1 + 3 files changed, 102 insertions(+) create mode 100644 db/migrations/20260104203627_playlist_case_insensitive_name.sql diff --git a/db/migrations/20260104203627_playlist_case_insensitive_name.sql b/db/migrations/20260104203627_playlist_case_insensitive_name.sql new file mode 100644 index 00000000..64b079cc --- /dev/null +++ b/db/migrations/20260104203627_playlist_case_insensitive_name.sql @@ -0,0 +1,99 @@ +-- +goose Up +-- Fix case-insensitive sorting for playlist names +create table playlist_dg_tmp +( + id varchar(255) not null + primary key, + name varchar(255) collate NOCASE default '' not null, + comment varchar(255) default '' not null, + duration real default 0 not null, + song_count integer default 0 not null, + public bool default FALSE not null, + created_at datetime, + updated_at datetime, + path string default '' not null, + sync bool default false not null, + size integer default 0 not null, + rules varchar, + evaluated_at datetime, + owner_id varchar(255) not null + constraint playlist_user_user_id_fk + references user + on update cascade on delete cascade +); + +insert into playlist_dg_tmp(id, name, comment, duration, song_count, public, created_at, updated_at, path, sync, size, + rules, evaluated_at, owner_id) +select id, name, comment, duration, song_count, public, created_at, updated_at, path, sync, size, rules, evaluated_at, + owner_id +from playlist; + +drop table playlist; + +alter table playlist_dg_tmp + rename to playlist; + +create index playlist_name + on playlist (name); + +create index playlist_created_at + on playlist (created_at); + +create index playlist_updated_at + on playlist (updated_at); + +create index playlist_evaluated_at + on playlist (evaluated_at); + +create index playlist_size + on playlist (size); + +-- +goose Down +-- Note: Downgrade loses the collation but preserves data +create table playlist_dg_tmp +( + id varchar(255) not null + primary key, + name varchar(255) default '' not null, + comment varchar(255) default '' not null, + duration real default 0 not null, + song_count integer default 0 not null, + public bool default FALSE not null, + created_at datetime, + updated_at datetime, + path string default '' not null, + sync bool default false not null, + size integer default 0 not null, + rules varchar, + evaluated_at datetime, + owner_id varchar(255) not null + constraint playlist_user_user_id_fk + references user + on update cascade on delete cascade +); + +insert into playlist_dg_tmp(id, name, comment, duration, song_count, public, created_at, updated_at, path, sync, size, + rules, evaluated_at, owner_id) +select id, name, comment, duration, song_count, public, created_at, updated_at, path, sync, size, rules, evaluated_at, + owner_id +from playlist; + +drop table playlist; + +alter table playlist_dg_tmp + rename to playlist; + +create index playlist_name + on playlist (name); + +create index playlist_created_at + on playlist (created_at); + +create index playlist_updated_at + on playlist (updated_at); + +create index playlist_evaluated_at + on playlist (evaluated_at); + +create index playlist_size + on playlist (size); diff --git a/persistence/collation_test.go b/persistence/collation_test.go index 7e114475..bb127657 100644 --- a/persistence/collation_test.go +++ b/persistence/collation_test.go @@ -32,6 +32,7 @@ var _ = Describe("Collation", func() { Entry("media_file.sort_title", "media_file", "sort_title"), Entry("media_file.sort_album_name", "media_file", "sort_album_name"), Entry("media_file.sort_artist_name", "media_file", "sort_artist_name"), + Entry("playlist.name", "playlist", "name"), Entry("radio.name", "radio", "name"), Entry("user.name", "user", "name"), ) @@ -53,6 +54,7 @@ var _ = Describe("Collation", func() { Entry("media_file.sort_album_name", "media_file", "coalesce(nullif(sort_album_name,''),order_album_name) collate nocase"), Entry("media_file.sort_artist_name", "media_file", "coalesce(nullif(sort_artist_name,''),order_artist_name) collate nocase"), Entry("media_file.path", "media_file", "path collate nocase"), + Entry("playlist.name", "playlist", "name collate nocase"), Entry("radio.name", "radio", "name collate nocase"), Entry("user.user_name", "user", "user_name collate nocase"), ) diff --git a/ui/src/playlist/PlaylistList.jsx b/ui/src/playlist/PlaylistList.jsx index c1856675..eae9d863 100644 --- a/ui/src/playlist/PlaylistList.jsx +++ b/ui/src/playlist/PlaylistList.jsx @@ -170,6 +170,7 @@ const PlaylistList = (props) => { } actions={} bulkActionButtons={!isXsmall && }