lib/chore_runner_ui/downloads_live.ex

defmodule ChoreRunnerUI.DownloadsLive do
  @moduledoc """
  A LiveView used in conjunction with ChoreRunner's regular functionality.
  Allows viewing and downloading of files generated by Chores.

  ## Usage
  Make sure that `Chore.Supervisor` is added in your application
  ```
  children = [
    {Phoenix.PubSub, name: MyApp.PubSub},
    {ChoreRunner, pubsub: MyApp.PubSub}
  ]
  ```
  A pubsub MUST BE RUNNING and configured for both the Chore supervisor and the ChoreRunner LiveViews for the Downloads UI to function.

  Make the Downloads LiveView and downloadable files accessible in your Phoenix web app by modifying the router.

  ### Router
  ```
  @chore_session %{
    "otp_app" => :my_app,
    "chore_root" => MyApp.Chores,
    "pubsub" => MyApp.PubSub
  }
  scope "/" do
    pipe_through :browser

    live_session :chores, session: @chore_session do
      live "/", ChoreRunnerUI.ChoreLive, :index
      live "/", ChoreRunnerUI.DownloadsLive, :index
    end

    forward "/chore_downloads", ChoreRunnerUI.DownloadsPlug
  end
  ```

  The `"chore_root"` key in the session should be the module root that all of your chore modules use.
  For example: if your root is `MyApp.Chores` your chore modules should be named like `MyApp.Chores.MyChore`

  Now you can visit the speficied url and start running chores!
  """
  use ChoreRunnerUI, :live
  alias Phoenix.PubSub
  alias ChoreRunner.Downloads
  alias ChoreRunner.Downloads.StorageService
  require Logger

  @impl true
  def mount(params, session, socket) do
    socket =
      assign(socket,
        pubsub: subscribe_to_pubsub(session),
        download_plug_path: Map.get(params, :download_plug_path, "/chores/download"),
        chore_live_path: Map.get(params, :chore_live_path, "/chores")
      )
      |> refresh_downloads()

    {:ok, socket}
  end

  @impl true
  def handle_event("delete", %{"id" => id}, socket) do
    with %{} = download <- StorageService.find_file(id) do
      Downloads.delete_download(download)
    end

    PubSub.broadcast(
      socket.assigns.pubsub,
      ChoreRunner.downloads_pubsub_topic(),
      :downloads_updated
    )

    {:noreply, refresh_downloads(socket)}
  end

  @impl true
  def handle_info(:downloads_updated, socket) do
    {:noreply, refresh_downloads(socket)}
  end

  defp refresh_downloads(socket), do: assign(socket, downloads: Downloads.list_downloads())

  defp subscribe_to_pubsub(%{"pubsub" => pubsub}) do
    Process.put(:chore_pubsub, pubsub)
    PubSub.subscribe(pubsub, ChoreRunner.downloads_pubsub_topic())
    pubsub
  end

  defp download_link(download_plug_path, download),
    do:
      ChoreRunner.Downloads.StorageService.file_url(download,
        download_plug_path: download_plug_path
      )
end