diff --git a/lib/pinchflat_web/components/core_components.ex b/lib/pinchflat_web/components/core_components.ex index ccdf0b9..58f785c 100644 --- a/lib/pinchflat_web/components/core_components.ex +++ b/lib/pinchflat_web/components/core_components.ex @@ -266,6 +266,7 @@ defmodule PinchflatWeb.CoreComponents do multiple pattern placeholder readonly required rows size step) slot :inner_block + slot :input_append def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do assigns @@ -424,20 +425,23 @@ defmodule PinchflatWeb.CoreComponents do <.label for={@id}> <%= @label %><%= @label_suffix %> - +
+ + <%= render_slot(@input_append) %> +
<.help :if={@help}><%= if @html_help, do: Phoenix.HTML.raw(@help), else: @help %> <.error :for={msg <- @errors}><%= msg %> diff --git a/lib/pinchflat_web/components/custom_components/button_components.ex b/lib/pinchflat_web/components/custom_components/button_components.ex index a80bb75..61a8ed1 100644 --- a/lib/pinchflat_web/components/custom_components/button_components.ex +++ b/lib/pinchflat_web/components/custom_components/button_components.ex @@ -89,4 +89,46 @@ defmodule PinchflatWeb.CustomComponents.ButtonComponents do """ end + + @doc """ + Render a button with an icon. Optionally include a tooltip. + + ## Examples + + <.icon_button icon_name="hero-check" tooltip="Complete" /> + """ + attr :icon_name, :string, required: true + attr :class, :string, default: "" + attr :tooltip, :string, default: nil + attr :rest, :global + + def icon_button(assigns) do + ~H""" +
+ +
+ + + <%= @tooltip %> +
+
+ """ + end end diff --git a/lib/pinchflat_web/controllers/settings/setting_html.ex b/lib/pinchflat_web/controllers/settings/setting_html.ex index 47ad97c..392975d 100644 --- a/lib/pinchflat_web/controllers/settings/setting_html.ex +++ b/lib/pinchflat_web/controllers/settings/setting_html.ex @@ -6,6 +6,7 @@ defmodule PinchflatWeb.Settings.SettingHTML do @doc """ Renders a setting form. """ + attr :conn, Plug.Conn, required: true attr :changeset, Ecto.Changeset, required: true attr :action, :string, required: true diff --git a/lib/pinchflat_web/controllers/settings/setting_html/apprise_server_live.ex b/lib/pinchflat_web/controllers/settings/setting_html/apprise_server_live.ex new file mode 100644 index 0000000..a482f36 --- /dev/null +++ b/lib/pinchflat_web/controllers/settings/setting_html/apprise_server_live.ex @@ -0,0 +1,55 @@ +defmodule Pinchflat.Settings.AppriseServerLive do + use PinchflatWeb, :live_view + + alias PinchflatWeb.Settings.SettingHTML + + def render(assigns) do + ~H""" + <.input + type="text" + id="setting_apprise_server" + name="setting[apprise_server]" + value={@value} + label="Apprise Server" + help={SettingHTML.apprise_server_help()} + html_help={true} + inputclass="font-mono text-sm mr-4" + placeholder="https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}" + phx-change="apprise_server_changed" + > + <:input_append> + <.icon_button icon_name={@icon_name} class="h-12 w-12" phx-click="send_apprise_test" tooltip={@tooltip} /> + + + """ + end + + def mount(_params, session, socket) do + new_assigns = %{ + value: session["value"], + icon_name: "hero-paper-airplane", + tooltip: "Send Test" + } + + {:ok, assign(socket, new_assigns)} + end + + def handle_event("send_apprise_test", _params, %{assigns: assigns} = socket) do + backend_runner().run([assigns.value], title: "Pinchflat Test", body: "This is a test message from Pinchflat") + Process.send_after(self(), :reset_button_icon, 4_000) + + {:noreply, assign(socket, %{icon_name: "hero-check", tooltip: "Sent!"})} + end + + def handle_event("apprise_server_changed", %{"setting" => setting}, socket) do + {:noreply, assign(socket, %{value: setting["apprise_server"]})} + end + + def handle_info(:reset_button_icon, socket) do + {:noreply, assign(socket, %{icon_name: "hero-paper-airplane", tooltip: "Send Test"})} + end + + defp backend_runner do + Application.get_env(:pinchflat, :apprise_runner) + end +end diff --git a/lib/pinchflat_web/controllers/settings/setting_html/setting_form.html.heex b/lib/pinchflat_web/controllers/settings/setting_html/setting_form.html.heex index 9958c5a..f8e5d7e 100644 --- a/lib/pinchflat_web/controllers/settings/setting_html/setting_form.html.heex +++ b/lib/pinchflat_web/controllers/settings/setting_html/setting_form.html.heex @@ -7,15 +7,13 @@ Notification Settings - <.input - field={f[:apprise_server]} - type="text" - label="Apprise Server" - help={apprise_server_help()} - html_help={true} - inputclass="font-mono text-sm" - placeholder="https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}" - /> +
+ <%= live_render( + @conn, + Pinchflat.Settings.AppriseServerLive, + session: %{"value" => f[:apprise_server].value} + ) %> +
<.button class="mt-10 mb-4 sm:mb-8 w-full sm:w-auto" rounding="rounded-lg">Save Settings diff --git a/lib/pinchflat_web/controllers/settings/setting_html/show.html.heex b/lib/pinchflat_web/controllers/settings/setting_html/show.html.heex index abe2938..9921bc9 100644 --- a/lib/pinchflat_web/controllers/settings/setting_html/show.html.heex +++ b/lib/pinchflat_web/controllers/settings/setting_html/show.html.heex @@ -7,6 +7,6 @@
- <.setting_form changeset={@changeset} action={~p"/settings"} /> + <.setting_form conn={@conn} changeset={@changeset} action={~p"/settings"} />
diff --git a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex index 8c56d97..b6bfd76 100644 --- a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex +++ b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex @@ -12,13 +12,7 @@ defmodule Pinchflat.Sources.MediaItemTableLive do def render(%{records: []} = assigns) do ~H"""
- + <.icon_button icon_name="hero-arrow-path" class="h-10 w-10" phx-click="reload_page" />

Nothing Here!

""" @@ -28,13 +22,7 @@ defmodule Pinchflat.Sources.MediaItemTableLive do ~H"""
- + <.icon_button icon_name="hero-arrow-path" class="h-10 w-10" phx-click="reload_page" tooltip="Refresh" /> Showing <%= length(@records) %> of <%= @total_record_count %> <.table rows={@records} table_class="text-white"> diff --git a/test/pinchflat_web/controllers/settings/apprise_server_live_test.exs b/test/pinchflat_web/controllers/settings/apprise_server_live_test.exs new file mode 100644 index 0000000..e09265f --- /dev/null +++ b/test/pinchflat_web/controllers/settings/apprise_server_live_test.exs @@ -0,0 +1,70 @@ +defmodule PinchflatWeb.Settings.AppriseServerLiveTest do + use PinchflatWeb.ConnCase + + import Mox + import Phoenix.LiveViewTest + + alias Pinchflat.Settings.AppriseServerLive + + setup :verify_on_exit! + + describe "initial rendering" do + test "renders the input", %{conn: conn} do + {:ok, _view, html} = live_isolated(conn, AppriseServerLive, session: create_session("")) + + assert html =~ ~s(input type="text" name="setting[apprise_server]") + end + + test "sets the initial value from the session", %{conn: conn} do + {:ok, _view, html} = live_isolated(conn, AppriseServerLive, session: create_session("cool-value")) + + assert html =~ ~s(value="cool-value") + end + + test "shows a relevant button icon", %{conn: conn} do + {:ok, _view, html} = live_isolated(conn, AppriseServerLive, session: create_session("")) + + assert html =~ "hero-paper-airplane" + refute html =~ "hero-check" + end + end + + describe "pressing the button" do + setup do + stub(AppriseRunnerMock, :run, fn _, _ -> {:ok, ""} end) + + :ok + end + + test "sends a test message to the specified server", %{conn: conn} do + expect(AppriseRunnerMock, :run, fn servers, args -> + assert servers == ["cool-value"] + assert args == [title: "Pinchflat Test", body: "This is a test message from Pinchflat"] + + {:ok, ""} + end) + + {:ok, view, _html} = live_isolated(conn, AppriseServerLive, session: create_session("cool-value")) + + assert view + |> element("button") + |> render_click() + end + + test "sets the button icon to a checkmark", %{conn: conn} do + {:ok, view, _html} = live_isolated(conn, AppriseServerLive, session: create_session("cool-value")) + + result = + view + |> element("button") + |> render_click() + + refute result =~ "hero-paper-airplane" + assert result =~ "hero-check" + end + end + + defp create_session(value) do + %{"value" => value} + end +end