lib/tailwind/phoenix/combined.ex

defmodule Tailwind.Phoenix.Combined do
  @moduledoc """
  Helper to make useful notifications to be used in LiveView controllers where
  Index and Show is combined.

  It implements handle_info/2 LiveView callbacks for events,
  updates the current assigns if a notification arrives.

  The value function gets passed the socket as first and only parameter.
  This way you can use any assign in the socket to use the correct query.

  ## Examples

  ```elixir
  defmodule MyWeb.MyDataLive.Combined do
    use MyWeb, :live_view

    use Tailwind.Phoenix.Combined,
      notifier: MyWeb.Notifier,
      key_index: :datas,
      key_show: :data,
      data: %My.MyData{},
      value_index: &list_datas/1,
      to: &Routes.my_data_path(&1, :index)

    ...
  end
  ```
  """

  @doc """
  When used, implements handle_info/2 for index.ex.
  """
  defmacro __using__(opts) do
    notifier = Keyword.get(opts, :notifier)
    index_assign_key = Keyword.get(opts, :key_index)
    show_assign_key = Keyword.get(opts, :key_show)
    data_pattern = Keyword.get(opts, :pattern)
    assign_value_fn = Keyword.get(opts, :value)
    return_to_fn = Keyword.get(opts, :to)

    quote do
      @impl true
      def handle_info({:deleted, unquote(data_pattern) = data} = msg, socket) do
        socket =
          if socket.assigns[unquote(show_assign_key)] == nil do
            socket
          else
            to = unquote(return_to_fn).(socket)

            socket
            |> Tailwind.Phoenix.redirect_if_id(socket.assigns[unquote(show_assign_key)].id, data,
              to: to
            )
            |> unquote(notifier).to_flash(msg)
          end

        socket =
          if socket.assigns[unquote(index_assign_key)] == nil do
            socket
          else
            socket
            |> unquote(notifier).to_flash(msg)
            |> update(unquote(index_assign_key), fn _ -> unquote(assign_value_fn).(socket) end)
          end

        {:noreply, socket}
      end

      @impl true
      def handle_info({action, unquote(data_pattern) = data} = msg, socket) do
        socket =
          if socket.assigns[unquote(show_assign_key)] == nil do
            socket
          else
            socket
            |> unquote(notifier).to_flash(msg)
            |> Tailwind.Phoenix.update_if_id(
              unquote(show_assign_key),
              socket.assigns[unquote(show_assign_key)].id,
              data
            )
          end

        socket =
          if socket.assigns[unquote(index_assign_key)] == nil do
            socket
          else
            socket
            |> unquote(notifier).to_flash(msg)
            |> update(unquote(index_assign_key), fn _ -> unquote(assign_value_fn).(socket) end)
          end

        {:noreply, socket}
      end

      defdelegate handle_info(data, socket), to: unquote(notifier)
    end
  end
end