diff --git a/CHANGELOG.md b/CHANGELOG.md index 645ee24..0fa9e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix datetime being reset at every changes - Fix account creation with a soft-deleted email +- Fix interaction list paging on resize ## v.3.0.0 diff --git a/assets/js/app.js b/assets/js/app.js index 6a1d9fb..10a15b2 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -515,6 +515,18 @@ Hooks.SlideSortable = { }; Hooks.InteractionDrag = { mounted() { + this.lastInteractionListHeight = null; + this.interactionListResizeObserver = new ResizeObserver(([entry]) => { + const height = window.matchMedia("(min-width: 1024px)").matches + ? Math.round(entry.contentRect.height) + : 0; + + if (height === this.lastInteractionListHeight) return; + this.lastInteractionListHeight = height; + this.pushEventTo(this.el, "interaction-list-resized", { height }); + }); + this.interactionListResizeObserver.observe(this.el); + this.el.addEventListener("dragstart", (e) => { const item = e.target.closest("[data-interaction-id]"); if (!item) return; @@ -546,6 +558,9 @@ Hooks.InteractionDrag = { .forEach((n) => n.classList.remove("opacity-40")); }); }, + destroyed() { + this.interactionListResizeObserver.disconnect(); + }, }; Hooks.OpenPresenter = { open(e) { diff --git a/lib/claper_web/live/event_live/manage.html.heex b/lib/claper_web/live/event_live/manage.html.heex index 5e23a05..da2da1d 100644 --- a/lib/claper_web/live/event_live/manage.html.heex +++ b/lib/claper_web/live/event_live/manage.html.heex @@ -540,7 +540,10 @@
-
+
<.live_component id="interaction-list" module={ClaperWeb.EventLive.ManageInteractionListComponent} diff --git a/lib/claper_web/live/event_live/manage_interaction_list_component.ex b/lib/claper_web/live/event_live/manage_interaction_list_component.ex index 175e83f..45f321c 100644 --- a/lib/claper_web/live/event_live/manage_interaction_list_component.ex +++ b/lib/claper_web/live/event_live/manage_interaction_list_component.ex @@ -2,17 +2,21 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do use ClaperWeb, :live_component @per_page 6 + @max_per_page 20 + @fixed_content_height 200 + @interaction_row_height 64 def update(assigns, socket) do page = Map.get(socket.assigns, :page, 0) + per_page = Map.get(socket.assigns, :per_page, @per_page) total = length(assigns.interactions) - max_page = max(0, ceil(total / @per_page) - 1) + max_page = max_page(total, per_page) page = min(page, max_page) {:ok, socket |> assign(assigns) - |> assign(page: page, per_page: @per_page)} + |> assign(page: page, per_page: per_page)} end def handle_event("prev-page", _, socket) do @@ -20,16 +24,38 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do end def handle_event("next-page", _, socket) do - max_page = max(0, ceil(length(socket.assigns.interactions) / @per_page) - 1) + max_page = max_page(length(socket.assigns.interactions), socket.assigns.per_page) {:noreply, assign(socket, page: min(max_page, socket.assigns.page + 1))} end + def handle_event("interaction-list-resized", %{"height" => height}, socket) + when is_integer(height) do + per_page = + height + |> Kernel.-(@fixed_content_height) + |> div(@interaction_row_height) + |> max(@per_page) + |> min(@max_per_page) + + first_interaction = socket.assigns.page * socket.assigns.per_page + + page = + min( + div(first_interaction, per_page), + max_page(length(socket.assigns.interactions), per_page) + ) + + {:noreply, assign(socket, page: page, per_page: per_page)} + end + defp paginated_interactions(interactions, page, per_page) do interactions |> Enum.drop(page * per_page) |> Enum.take(per_page) end + defp max_page(total, per_page), do: max(0, ceil(total / per_page) - 1) + def render(assigns) do assigns = assign(assigns, @@ -41,7 +67,8 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
Floki.attribute("#interactions-pane", "class") + |> List.first() + |> String.split() + + interaction_list_classes = + document + |> Floki.attribute("#interaction-drag-list", "class") + |> List.first() + |> String.split() + + assert "lg:flex" in interaction_pane_classes + assert "lg:min-h-0" in interaction_pane_classes + assert "lg:overflow-hidden" in interaction_pane_classes + assert "lg:flex-1" in interaction_list_classes + assert "lg:overflow-y-auto" in interaction_list_classes + assert has_element?(manage_live, ~s(#interaction-drag-list button[phx-click="next-page"])) + + manage_live + |> element(~s(#interaction-drag-list button[phx-click="next-page"])) + |> render_click() + + assert render(manage_live) =~ "2 / 2" + end + + test "shows more interactions when the panel is taller", %{ + conn: conn, + presentation_file: presentation_file + } do + for index <- 1..13 do + poll_fixture(%{ + presentation_file_id: presentation_file.id, + title: "Poll #{index}" + }) + end + + {:ok, manage_live, _html} = live(conn, ~p"/e/#{presentation_file.event.code}/manage") + + assert manage_live + |> render() + |> Floki.parse_document!() + |> Floki.find("[data-interaction-id]") + |> length() == 6 + + manage_live + |> element("#interaction-drag-list") + |> render_hook("interaction-list-resized", %{"height" => 900}) + + html = render(manage_live) + + assert html + |> Floki.parse_document!() + |> Floki.find("[data-interaction-id]") + |> length() == 10 + + assert html =~ "1 / 2" + end + test "prompts to regenerate missing thumbnails and starts regeneration", %{ conn: conn, presentation_file: presentation_file