Hooked up pagination for downloaded media

This commit is contained in:
Kieran Eglin 2024-04-17 10:15:06 -07:00
parent 9f491d4774
commit 4e1b18dfed
No known key found for this signature in database
GPG key ID: 193984967FCF432D
7 changed files with 122 additions and 66 deletions

View file

@ -3,7 +3,11 @@ defmodule Pinchflat.Utils.NumberUtils do
Utility methods for working with numbers Utility methods for working with numbers
""" """
# TODO: test @doc """
Clamps a number between a minimum and maximum value
Returns integer() | float()
"""
def clamp(num, minimum, maximum) do def clamp(num, minimum, maximum) do
num num
|> max(minimum) |> max(minimum)

View file

@ -53,10 +53,17 @@ defmodule PinchflatWeb.CustomComponents.TableComponents do
""" """
end end
@doc """
Renders simple pagination controls for a table in a liveview.
## Examples
<.live_pagination_controls page_number={@page} total_pages={@total_pages} />
"""
attr :page_number, :integer, default: 1 attr :page_number, :integer, default: 1
attr :total_pages, :integer, default: 1 attr :total_pages, :integer, default: 1
def pagination_controls(assigns) do def live_pagination_controls(assigns) do
~H""" ~H"""
<nav> <nav>
<ul class="flex flex-wrap items-center"> <ul class="flex flex-wrap items-center">

View file

@ -6,9 +6,7 @@ defmodule PinchflatWeb.Sources.SourceController do
alias Pinchflat.Repo alias Pinchflat.Repo
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.MediaQuery
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.Media.MediaQuery
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.Downloading.DownloadingHelpers
alias Pinchflat.SlowIndexing.SlowIndexingHelpers alias Pinchflat.SlowIndexing.SlowIndexingHelpers
@ -60,29 +58,7 @@ defmodule PinchflatWeb.Sources.SourceController do
|> Tasks.list_tasks_for(nil, [:executing, :available, :scheduled, :retryable]) |> Tasks.list_tasks_for(nil, [:executing, :available, :scheduled, :retryable])
|> Repo.preload(:job) |> Repo.preload(:job)
pending_media = render(conn, :show, source: source, pending_tasks: pending_tasks)
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.where_pending_download()
|> order_by(desc: :id)
|> limit(100)
|> Repo.all()
downloaded_media =
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.with_media_filepath()
|> order_by(desc: :id)
|> limit(100)
|> Repo.all()
render(conn, :show,
source: source,
pending_tasks: pending_tasks,
pending_media: pending_media,
downloaded_media: downloaded_media,
total_downloaded: total_downloaded_for(source)
)
end end
def edit(conn, %{"id" => id}) do def edit(conn, %{"id" => id}) do
@ -151,13 +127,6 @@ defmodule PinchflatWeb.Sources.SourceController do
|> Repo.all() |> Repo.all()
end end
defp total_downloaded_for(source) do
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.with_media_filepath()
|> Repo.aggregate(:count, :id)
end
defp get_onboarding_layout do defp get_onboarding_layout do
if Settings.get!(:onboarding) do if Settings.get!(:onboarding) do
{Layouts, :onboarding} {Layouts, :onboarding}

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.PendingTableLive do defmodule Pinchflat.Sources.MediaItemTableLive do
use PinchflatWeb, :live_view use PinchflatWeb, :live_view
import Ecto.Query, warn: false import Ecto.Query, warn: false
@ -33,7 +33,7 @@ defmodule Pinchflat.PendingTableLive do
</:col> </:col>
</.table> </.table>
<section class="flex justify-center mt-5"> <section class="flex justify-center mt-5">
<.pagination_controls page_number={@page} total_pages={@total_pages} /> <.live_pagination_controls page_number={@page} total_pages={@total_pages} />
</section> </section>
</div> </div>
""" """
@ -41,8 +41,9 @@ defmodule Pinchflat.PendingTableLive do
def mount(_params, session, socket) do def mount(_params, session, socket) do
page = 1 page = 1
media_state = session["media_state"]
source = Sources.get_source!(session["source_id"]) source = Sources.get_source!(session["source_id"])
base_query = generate_base_query(source) base_query = generate_base_query(source, media_state)
pagination_attrs = fetch_pagination_attributes(base_query, page) pagination_attrs = fetch_pagination_attributes(base_query, page)
{:ok, assign(socket, Map.merge(pagination_attrs, %{base_query: base_query, source: source}))} {:ok, assign(socket, Map.merge(pagination_attrs, %{base_query: base_query, source: source}))}
@ -58,20 +59,13 @@ defmodule Pinchflat.PendingTableLive do
defp fetch_pagination_attributes(base_query, page) do defp fetch_pagination_attributes(base_query, page) do
total_record_count = Repo.aggregate(base_query, :count, :id) total_record_count = Repo.aggregate(base_query, :count, :id)
total_pages = ceil(total_record_count / @limit) total_pages = max(ceil(total_record_count / @limit), 1)
page = NumberUtils.clamp(page, 1, total_pages) page = NumberUtils.clamp(page, 1, total_pages)
records = fetch_records(base_query, page) records = fetch_records(base_query, page)
%{page: page, total_pages: total_pages, records: records, total_record_count: total_record_count} %{page: page, total_pages: total_pages, records: records, total_record_count: total_record_count}
end end
defp generate_base_query(source) do
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.where_pending_download()
|> order_by(desc: :id)
end
defp fetch_records(base_query, page) do defp fetch_records(base_query, page) do
offset = (page - 1) * @limit offset = (page - 1) * @limit
@ -80,4 +74,18 @@ defmodule Pinchflat.PendingTableLive do
|> offset(^offset) |> offset(^offset)
|> Repo.all() |> Repo.all()
end end
defp generate_base_query(source, "pending") do
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.where_pending_download()
|> order_by(desc: :id)
end
defp generate_base_query(source, "downloaded") do
MediaQuery.new()
|> MediaQuery.for_source(source)
|> MediaQuery.with_media_filepath()
|> order_by(desc: :id)
end
end end

View file

@ -37,29 +37,18 @@
</div> </div>
</:tab> </:tab>
<:tab title="Pending Media"> <:tab title="Pending Media">
<%= live_render(@conn, Pinchflat.PendingTableLive, session: %{"source_id" => @source.id}) %> <%= live_render(
@conn,
Pinchflat.Sources.MediaItemTableLive,
session: %{"source_id" => @source.id, "media_state" => "pending"}
) %>
</:tab> </:tab>
<:tab title="Downloaded Media"> <:tab title="Downloaded Media">
<%= if match?([_|_], @downloaded_media) do %> <%= live_render(
<h4 class="text-white text-lg mb-6">Shows a maximum of 100 media items (<%= @total_downloaded %> total)</h4> @conn,
<.table rows={@downloaded_media} table_class="text-black dark:text-white"> Pinchflat.Sources.MediaItemTableLive,
<:col :let={media_item} label="Title"> session: %{"source_id" => @source.id, "media_state" => "downloaded"}
<.subtle_link href={~p"/sources/#{@source.id}/media/#{media_item.id}"}> ) %>
<%= StringUtils.truncate(media_item.title, 50) %>
</.subtle_link>
</:col>
<:col :let={media_item} label="" class="flex place-content-evenly">
<.icon_link href={~p"/sources/#{@source.id}/media/#{media_item.id}"} icon="hero-eye" class="mx-1" />
<.icon_link
href={~p"/sources/#{@source.id}/media/#{media_item.id}/edit"}
icon="hero-pencil-square"
class="mx-1"
/>
</:col>
</.table>
<% else %>
<p class="text-black dark:text-white">Nothing Here!</p>
<% end %>
</:tab> </:tab>
<:tab title="Pending Tasks"> <:tab title="Pending Tasks">
<%= if match?([_|_], @pending_tasks) do %> <%= if match?([_|_], @pending_tasks) do %>

View file

@ -0,0 +1,19 @@
defmodule Pinchflat.Utils.NumberUtilsTest do
use ExUnit.Case, async: true
alias Pinchflat.Utils.NumberUtils
describe "clamp/3" do
test "returns the minimum when the number is less than the minimum" do
assert NumberUtils.clamp(1, 2, 3) == 2
end
test "returns the maximum when the number is greater than the maximum" do
assert NumberUtils.clamp(4, 2, 3) == 3
end
test "returns the number when it is between the minimum and maximum" do
assert NumberUtils.clamp(2, 1, 3) == 2
end
end
end

View file

@ -0,0 +1,60 @@
defmodule PinchflatWeb.Sources.MediaItemTableLiveTest do
use PinchflatWeb.ConnCase
import Phoenix.LiveViewTest
import Pinchflat.MediaFixtures
import Pinchflat.SourcesFixtures
alias Pinchflat.Sources.MediaItemTableLive
setup do
source = source_fixture()
{:ok, source: source}
end
describe "initial rendering" do
test "shows message when no records", %{conn: conn, source: source} do
{:ok, _view, html} = live_isolated(conn, MediaItemTableLive, session: create_session(source))
assert html =~ "Nothing Here!"
refute html =~ "Showing"
end
test "shows records when present", %{conn: conn, source: source} do
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
{:ok, _view, html} = live_isolated(conn, MediaItemTableLive, session: create_session(source))
assert html =~ "Showing 1 of 1"
assert html =~ "Title"
assert html =~ media_item.title
end
end
describe "media_state" do
test "shows pending media when pending", %{conn: conn, source: source} do
downloaded_media_item = media_item_fixture(source_id: source.id)
pending_media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
{:ok, _view, html} = live_isolated(conn, MediaItemTableLive, session: create_session(source, "pending"))
assert html =~ pending_media_item.title
refute html =~ downloaded_media_item.title
end
test "shows downloaded media when downloaded", %{conn: conn, source: source} do
downloaded_media_item = media_item_fixture(source_id: source.id)
pending_media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
{:ok, _view, html} = live_isolated(conn, MediaItemTableLive, session: create_session(source, "downloaded"))
assert html =~ downloaded_media_item.title
refute html =~ pending_media_item.title
end
end
defp create_session(source, media_state \\ "pending") do
%{"source_id" => source.id, "media_state" => media_state}
end
end