diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex index 08ab6b8a..e98632ec 100644 --- a/lib/music_library/records.ex +++ b/lib/music_library/records.ex @@ -35,6 +35,14 @@ defmodule MusicLibrary.Records do Repo.aggregate(Record, :count) end + def search_records_count(query) do + q = + from r in Record, + where: like(r.title, ^"%#{query}%") or like(r.artists, ^"%#{query}%") + + Repo.aggregate(q, :count) + end + def get_record!(id), do: Repo.get!(Record, id) def get_image!(id) do diff --git a/lib/music_library_web/components/pagination.ex b/lib/music_library_web/components/pagination.ex index 07212418..2c92d5d0 100644 --- a/lib/music_library_web/components/pagination.ex +++ b/lib/music_library_web/components/pagination.ex @@ -1,7 +1,6 @@ defmodule MusicLibraryWeb.Pagination do use Phoenix.Component - # alias Phoenix.LiveView.JS use Gettext, backend: MusicLibraryWeb.Gettext attr :pagination_params, :map, required: true @@ -39,11 +38,13 @@ defmodule MusicLibraryWeb.Pagination do :if={@page_links.prev_page} page_number={@page_links.prev_page} page_size={@pagination_params.page_size} + query={@pagination_params.query} /> <.numbered_link :for={page_number <- @page_links.visible_left_pages} page_number={page_number} page_size={@pagination_params.page_size} + query={@pagination_params.query} /> <.separator :if={@page_links.left_separator} /> <.numbered_link @@ -51,17 +52,20 @@ defmodule MusicLibraryWeb.Pagination do page_number={page_number} active={page_number == @pagination_params.page} page_size={@pagination_params.page_size} + query={@pagination_params.query} /> <.separator :if={@page_links.right_separator} /> <.numbered_link :for={page_number <- @page_links.visible_right_pages} page_number={page_number} page_size={@pagination_params.page_size} + query={@pagination_params.query} /> <.next_link :if={@page_links.next_page} page_number={@page_links.next_page} page_size={@pagination_params.page_size} + query={@pagination_params.query} /> @@ -72,12 +76,13 @@ defmodule MusicLibraryWeb.Pagination do attr :page_number, :integer, required: true attr :page_size, :integer, required: true + attr :query, :string, required: true defp next_link(assigns) do ~H""" <.link class="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)} + patch={"?" <> encode_query(page: @page_number, page_size: @page_size, query: @query)} > Next @@ -93,12 +98,13 @@ defmodule MusicLibraryWeb.Pagination do attr :page_number, :integer, required: true attr :page_size, :integer, required: true + attr :query, :string, required: true defp prev_link(assigns) do ~H""" <.link class="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)} + patch={"?" <> encode_query(page: @page_number, page_size: @page_size, query: @query)} > Previous @@ -123,6 +129,7 @@ defmodule MusicLibraryWeb.Pagination do attr :page_number, :integer, required: true attr :page_size, :integer, required: true attr :active, :boolean, default: false + attr :query, :string, required: true defp numbered_link(assigns) when assigns.active do ~H""" @@ -136,31 +143,17 @@ defmodule MusicLibraryWeb.Pagination do ~H""" <.link class="relative hidden items-center first:rounded-l-md last:rounded-r-md px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 md:inline-flex" - patch={"?" <> URI.encode_query(page: @page_number, page_size: @page_size)} + patch={"?" <> encode_query(page: @page_number, page_size: @page_size, query: @query)} > <%= @page_number %> """ end - def get_pagination_params(params, total_records) do - %{ - total_entries: total_records, - page: parse_int_or_default(params["page"], 1), - page_size: parse_int_or_default(params["page_size"], 20) - } - end - def page_to_offset(page, per_page) do (page - 1) * per_page end - defp parse_int_or_default(nil, default), do: default - - defp parse_int_or_default(value, _default) when is_binary(value) do - String.to_integer(value) - end - defp total_pages(total_entries, page_size) do without_remainder = div(total_entries, page_size) @@ -171,6 +164,12 @@ defmodule MusicLibraryWeb.Pagination do end end + defp encode_query(params) do + params + |> Enum.filter(fn {_, v} -> v not in ["", nil] end) + |> URI.encode_query() + end + @visible_left_pages 3 @visible_right_pages 3 @middle "..." @@ -208,6 +207,7 @@ defmodule MusicLibraryWeb.Pagination do visible_right_pages = Enum.take(right_pages, -@visible_right_pages) %{ + query: pagination_params.query, total_pages: total_pages, prev_page: prev_page, visible_left_pages: visible_left_pages, diff --git a/lib/music_library_web/live/record_live/index.ex b/lib/music_library_web/live/record_live/index.ex index 1405095c..ef05d721 100644 --- a/lib/music_library_web/live/record_live/index.ex +++ b/lib/music_library_web/live/record_live/index.ex @@ -4,17 +4,28 @@ defmodule MusicLibraryWeb.RecordLive.Index do alias MusicLibrary.Records + @default_records_list_params %{ + query: "", + page: 1, + page_size: 20 + } + @impl true def mount(params, _session, socket) do - total_records = Records.count_records() + query = params["query"] || "" + total_records = Records.search_records_count(query) - pagination_params = get_pagination_params(params, total_records) - offset = page_to_offset(pagination_params.page, pagination_params.page_size) - records = Records.list_records(limit: pagination_params.page_size, offset: offset) + record_list_params = + @default_records_list_params + |> merge_query(params["query"]) + |> merge_pagination(params, total_records) + + offset = page_to_offset(record_list_params.page, record_list_params.page_size) + records = Records.search_records(query, limit: record_list_params.page_size, offset: offset) {:ok, socket - |> assign(:pagination_params, pagination_params) + |> assign(:record_list_params, record_list_params) |> stream(:records, records)} end @@ -41,19 +52,20 @@ defmodule MusicLibraryWeb.RecordLive.Index do |> assign(:page_title, "Listing Records") |> assign(:record, nil) - total_records = Records.count_records() - pagination_params = get_pagination_params(params, total_records) + query = params["query"] || socket.assigns.record_list_params.query + total_records = Records.search_records_count(query) - if pagination_params != socket.assigns.pagination_params do - offset = page_to_offset(pagination_params.page, pagination_params.page_size) - records = Records.list_records(limit: pagination_params.page_size, offset: offset) + record_list_params = + socket.assigns.record_list_params + |> merge_query(params["query"]) + |> merge_pagination(params, total_records) - new_socket - |> assign(:pagination_params, pagination_params) - |> stream(:records, records, reset: true) - else - new_socket - end + offset = page_to_offset(record_list_params.page, record_list_params.page_size) + records = Records.search_records(query, limit: record_list_params.page_size, offset: offset) + + new_socket + |> assign(:record_list_params, record_list_params) + |> stream(:records, records, reset: true) end @impl true @@ -73,6 +85,16 @@ defmodule MusicLibraryWeb.RecordLive.Index do {:noreply, stream_delete(socket, :records, record)} end + def handle_event("search", %{"query" => query}, socket) do + qs = + @default_records_list_params + |> Map.put(:query, query) + |> Map.take([:query, :page, :page_size]) + |> URI.encode_query() + + {:noreply, push_patch(socket, to: ~s"/records?#{qs}")} + end + def handle_event("import", %{"id" => musicbrainz_id}, socket) do case Records.import_from_musicbrainz(musicbrainz_id) do {:ok, record} -> @@ -94,6 +116,28 @@ defmodule MusicLibraryWeb.RecordLive.Index do end end + defp merge_query(record_list_params, nil), do: record_list_params + + defp merge_query(record_list_params, query) do + Map.put(record_list_params, :query, query) + 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)) + |> Map.put( + :page_size, + parse_int_or_default(params["page_size"], record_list_params.page_size) + ) + |> Map.put(:total_entries, total_records) + end + + defp parse_int_or_default(nil, default), do: default + + defp parse_int_or_default(value, _default) when is_binary(value) do + String.to_integer(value) + end + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) defp musicbrainz_url(record) do diff --git a/lib/music_library_web/live/record_live/index.html.heex b/lib/music_library_web/live/record_live/index.html.heex index cee5321b..e700a173 100644 --- a/lib/music_library_web/live/record_live/index.html.heex +++ b/lib/music_library_web/live/record_live/index.html.heex @@ -1,11 +1,22 @@ -<.header> - Listing Records - <:actions> + + All Records + + + + <.link patch={~p"/records/import"}> <.button>Import from MusicBrainz - - + + <.table id="records" @@ -62,4 +73,4 @@ /> -<.pagination pagination_params={@pagination_params} /> +<.pagination pagination_params={@record_list_params} />