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