5.1 KiB · text History 9c0fc16
defmodule LambentExWeb.MachinesLive.Index do
  use LambentExWeb, :live_view

  alias LambentExWeb.MachinesLive.Index
  import LambentEx.Utils.Color, only: [hex: 1]

  @pubsub_name LambentEx.PubSub
  @pubsub_topic_fh "machine:"

  use Phoenix.LiveEditableView

  alias LambentEx.LiveEdit

  @speeds %{
    # 1 => :THOU,
    # 5 => :FTHOU,
    10 => :HUNDREDTHS,
    50 => :FHUNDREDTHS,
    100 => :TENTHS,
    500 => :FTENTHS,
    1000 => :ONES,
    2000 => :TWOS,
    5000 => :FIVES,
    10_000 => :TENS,
    20_000 => :TWENTYS,
    30_000 => :THIRTYS,
    60_000 => :MINS
  }

  @impl true
  def mount(_params, _session, socket) do
    Phoenix.PubSub.subscribe(@pubsub_name, "machines_idx")
    Phoenix.PubSub.subscribe(@pubsub_name, LambentEx.Machine.gone_topic())
    Phoenix.PubSub.subscribe(@pubsub_name, @pubsub_topic_fh)

    {:ok,
     socket
     |> assign(:machine, nil)
     |> assign(:machines, %{})
     |> assign(:previews, %{})
     |> assign(:nonew, true)
     |> assign(:newctrl, [
       %{link: ~p"/cfg/machines/new", icon: "plus"},
       %{link: ~p"/cfg/machines/library", icon: "book"}
     ])}
  end

  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :index, _params) do
    socket
    |> assign(:page_link, "Machines")
    |> assign(:page_title, "Machine Library")
    |> assign(:source, nil)
  end

  defp apply_action(socket, :new, params) do
    # opened from the catalog, the form starts on that entry's configuration
    machine =
      case LambentEx.Schema.Machines.from_library(params["cls"], params["entry"]) do
        nil -> %LambentEx.Schema.Machines{}
        prefilled -> prefilled
      end

    socket
    |> assign(:id, :new)
    |> assign(:page_link, "Machines")
    |> assign(
      :page_title,
      if(params["entry"], do: "Edit #{params["entry"]}", else: "New Machine")
    )
    |> assign(:machine, machine)
  end

  defp apply_action(socket, :library, _params) do
    socket
    |> assign(:id, :library)
    |> assign(:page_link, "Machines")
    |> assign(:page_title, "Machine Library")
    |> assign(:machine, [])
  end

  def handle_info({:machines_pub, machine}, socket) do
    #    IO.inspect(machine)
    {:noreply,
     socket |> assign(:machines, socket.assigns.machines |> Map.put(machine[:id], machine))}
  end

  def handle_info({:machines_gone, name}, socket) do
    {:noreply, drop_machine(socket, name)}
  end

  def handle_info({:firehose, {name, data}}, socket) do
    {:noreply, socket |> assign(:previews, socket.assigns.previews |> Map.put(name, data))}
  end

  def handle_event("faster", data, socket) do
    LambentEx.Machine.faster(data["id"])
    {:noreply, socket}
  end

  def handle_event("slower", data, socket) do
    LambentEx.Machine.slower(data["id"])
    {:noreply, socket}
  end

  def handle_event("dimmer", data, socket) do
    LambentEx.Machine.dimmer(data["id"])
    {:noreply, socket}
  end

  def handle_event("brighter", data, socket) do
    LambentEx.Machine.brighter(data["id"])
    {:noreply, socket}
  end

  def handle_event("quit", params, socket) do
    LambentEx.Machine.quit(params["tgt"])
    {:noreply, drop_machine(socket, params["tgt"])}
  end

  def handle_event("persist", data, socket) do
    LambentEx.Machine.persist(data["tgt"])
    {:noreply, socket}
  end

  def handle_event("library-edit", %{"cls" => cls, "entry" => entry}, socket) do
    {:noreply, push_patch(socket, to: ~p"/cfg/machines/new?cls=#{cls}&entry=#{entry}")}
  end

  # the class and entry come from the page, so anything not in the catalog is
  # dropped rather than made into atoms
  def handle_event("library-add", %{"cls" => cls, "entry" => entry}, socket) do
    case LambentEx.Machines.Library.entry(cls, entry) do
      nil ->
        {:noreply, socket}

      spec ->
        opts = spec[:mach_opts] || []
        LambentEx.MachineSupervisor.start_child(spec[:machine], spec[:args], entry, opts)
        {:noreply, socket}
    end
  end

  def handle_info(:update_library, socket) do
    send_update(LambentEx.MachinesLive.LibraryFormComponent, id: :library)
    {:noreply, socket}
  end

  defp spd(val) do
    @speeds[val]
  end

  def bgt(val) do
    (val / 255 * 100) |> trunc
  end

  # the listing is keyed by id (step x name), not by the name a quit is sent
  # with, so it is matched on the name. the keys of a :full machine are listed
  # under their own names, but all carry the machine's in their opts
  defp drop_machine(socket, name) do
    machines =
      socket.assigns.machines
      |> Enum.reject(fn {_id, m} -> m[:name] == name or m[:opts][:name] == name end)
      |> Map.new()

    socket
    |> assign(:machines, machines)
    |> assign(:previews, socket.assigns.previews |> Map.delete(name))
  end

  # the machine sends [r, g, b] pixels, but whatever shape turns up is cut back
  # into pixels here rather than taking the page down with a FunctionClauseError
  defp preview(pile, name) do
    pile
    |> Map.get(name, [])
    |> List.flatten()
    |> Enum.chunk_every(3, 3, :discard)
    |> Enum.map(fn [r, g, b] -> "##{hex(r)}#{hex(g)}#{hex(b)}" end)
  end
end