From 423d1e7764d076f7fac1a3e31f38e870db2bfcd1 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Mon, 26 May 2025 23:12:34 +0100 Subject: [PATCH] Can sort wishlisted records by insertion --- lib/music_library/records.ex | 7 + .../live/wishlist_live/index.ex | 19 ++ .../live/wishlist_live/index.html.heex | 29 ++ priv/gettext/default.pot | 6 + priv/gettext/en/LC_MESSAGES/default.po | 6 + ...add_timestamps_to_records_search_index.exs | 303 ++++++++++++++++++ 6 files changed, 370 insertions(+) create mode 100644 priv/repo/migrations/20250526220808_add_timestamps_to_records_search_index.exs diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex index e22e3496..3b14b77d 100644 --- a/lib/music_library/records.ex +++ b/lib/music_library/records.ex @@ -70,6 +70,13 @@ defmodule MusicLibrary.Records do {:desc, r.purchased_at}, order_alphabetically() ]) + + :insertion -> + initial_search + |> order_by([r], [ + {:desc, r.inserted_at}, + order_alphabetically() + ]) end Enum.reduce(parsed_query, search_with_order, fn diff --git a/lib/music_library_web/live/wishlist_live/index.ex b/lib/music_library_web/live/wishlist_live/index.ex index 03781949..4272628d 100644 --- a/lib/music_library_web/live/wishlist_live/index.ex +++ b/lib/music_library_web/live/wishlist_live/index.ex @@ -47,11 +47,13 @@ defmodule MusicLibraryWeb.WishlistLive.Index do defp apply_action(socket, :index, params) do query = params["query"] || "" + order = parse_order(params["order"] || "insertion") total_records = Wishlist.search_records_count(query) record_list_params = @default_records_list_params |> merge_query(query) + |> merge_order(order) |> merge_pagination(params, total_records) load_and_assign_records(socket, record_list_params) @@ -154,6 +156,10 @@ defmodule MusicLibraryWeb.WishlistLive.Index do Map.put(record_list_params, :query, query) end + defp merge_order(record_list_params, order) do + Map.put(record_list_params, :order, order) + end + defp merge_pagination(record_list_params, params, total_records) do record_list_params |> Map.put(:page, parse_int_or_default(params["page"], record_list_params.page)) @@ -170,6 +176,19 @@ defmodule MusicLibraryWeb.WishlistLive.Index do String.to_integer(value) end + defp parse_order("alphabetical"), do: :alphabetical + defp parse_order("insertion"), do: :insertion + + defp order_path(record_list_params, order) do + qs = + record_list_params + |> Map.take([:query]) + |> Map.put(:order, order) + |> Enum.filter(fn {_, v} -> v not in ["", nil] end) + + ~p"/wishlist?#{qs}" + end + defp back_path(record_list_params) do qs = record_list_params diff --git a/lib/music_library_web/live/wishlist_live/index.html.heex b/lib/music_library_web/live/wishlist_live/index.html.heex index 438fbe1b..d78d0c24 100644 --- a/lib/music_library_web/live/wishlist_live/index.html.heex +++ b/lib/music_library_web/live/wishlist_live/index.html.heex @@ -18,6 +18,35 @@ + +
+ + <.button + patch={order_path(@record_list_params, :insertion)} + as="link" + size="sm" + class={[ + "rounded-l-md rounded-r-none", + @record_list_params.order == :insertion && "!bg-zinc-100 dark:!bg-zinc-700" + ]} + > + <.icon name="hero-star" class="mr-1 h-4 w-4" aria-hidden="true" data-slot="icon" /> + {gettext("Insertion")} + + <.button + patch={order_path(@record_list_params, :alphabetical)} + as="link" + size="sm" + class={[ + "-ml-px rounded-r-md rounded-l-none", + @record_list_params.order == :alphabetical && "!bg-zinc-100 dark:!bg-zinc-700" + ]} + > + <.icon name="hero-user-solid" class="mr-1 h-4 w-4" aria-hidden="true" data-slot="icon" /> + {gettext("A->Z")} + + +
<.record_list diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index b6158dfe..554a2a1b 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -418,6 +418,7 @@ msgid "Error loading play count" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex +#: lib/music_library_web/live/wishlist_live/index.html.heex #, elixir-autogen, elixir-format msgid "A->Z" msgstr "" @@ -876,3 +877,8 @@ msgstr "" #, elixir-autogen, elixir-format msgid "Download" msgstr "" + +#: lib/music_library_web/live/wishlist_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Insertion" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 005b4465..94997891 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -418,6 +418,7 @@ msgid "Error loading play count" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex +#: lib/music_library_web/live/wishlist_live/index.html.heex #, elixir-autogen, elixir-format msgid "A->Z" msgstr "" @@ -876,3 +877,8 @@ msgstr "" #, elixir-autogen, elixir-format msgid "Download" msgstr "" + +#: lib/music_library_web/live/wishlist_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Insertion" +msgstr "" diff --git a/priv/repo/migrations/20250526220808_add_timestamps_to_records_search_index.exs b/priv/repo/migrations/20250526220808_add_timestamps_to_records_search_index.exs new file mode 100644 index 00000000..a0625dbb --- /dev/null +++ b/priv/repo/migrations/20250526220808_add_timestamps_to_records_search_index.exs @@ -0,0 +1,303 @@ +defmodule MusicLibrary.Repo.Migrations.AddTimestampsToRecordsSearchIndex do + use Ecto.Migration + + def up do + execute "DROP TRIGGER IF EXISTS records_after_update" + execute "DROP TRIGGER IF EXISTS records_after_insert" + execute "DROP TABLE records_search_index" + + flush() + + execute """ + CREATE VIRTUAL TABLE records_search_index USING fts5( + id UNINDEXED, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids UNINDEXED, + included_release_group_ids UNINDEXED, + cover_hash UNINDEXED, + purchased_at UNINDEXED, + release_date, + inserted_at UNINDEXED, + updated_at UNINDEXED + ); + """ + + flush() + + execute """ + CREATE TRIGGER records_after_insert + AFTER INSERT ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + FROM records + WHERE NEW.id = records.id; + END; + """ + + execute """ + CREATE TRIGGER records_after_update + AFTER UPDATE ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + FROM records + WHERE NEW.id = records.id; + END; + """ + + flush() + + execute """ + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date, + inserted_at, + updated_at + FROM records; + """ + end + + def down do + execute "DROP TRIGGER IF EXISTS records_after_update" + execute "DROP TRIGGER IF EXISTS records_after_insert" + execute "DROP TABLE records_search_index" + + flush() + + execute """ + CREATE VIRTUAL TABLE records_search_index USING fts5( + id UNINDEXED, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids UNINDEXED, + included_release_group_ids UNINDEXED, + cover_hash UNINDEXED, + purchased_at UNINDEXED, + release_date + ); + """ + + flush() + + execute """ + CREATE TRIGGER records_after_insert + AFTER INSERT ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + execute """ + CREATE TRIGGER records_after_update + AFTER UPDATE ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + flush() + + execute """ + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records; + """ + end +end