defmodule LivebookWeb.SessionLive.InsertImageComponent do use LivebookWeb, :live_component import Ecto.Changeset alias Livebook.FileSystem @impl true def mount(socket) do {:ok, socket |> assign(changeset: changeset(), error_message: nil) |> allow_upload(:image, accept: ~w(.jpg .jpeg .png .gif .svg), max_entries: 1, max_file_size: 5_000_000 )} end defp changeset(attrs \\ %{}) do data = %{name: nil} types = %{name: :string} cast({data, types}, attrs, [:name]) |> validate_required([:name]) |> Livebook.Notebook.validate_file_entry_name(:name) end @impl true def render(assigns) do ~H"""

Insert image

Invalid image file. The image must be either GIF, JPEG, SVG or PNG and cannot exceed 5MB in size.
<%= @error_message %>
<.live_img_preview entry={entry} class="max-h-80 m-auto" />
<.form :let={f} for={@changeset} as={:data} phx-change="validate" phx-submit="save" phx-target={@myself} >
<.file_drop_input upload={@uploads.image} label="File" on_clear={JS.push("clear_file", target: @myself)} /> <.text_field field={f[:name]} label="Name" autocomplete="off" phx-debounce="blur" />
<.link patch={@return_to} class="button-base button-outlined-gray"> Cancel
""" end @impl true def handle_event("validate", %{"data" => data} = params, socket) do upload_entries = socket.assigns.uploads.image.entries data = case {params["_target"], data["name"], upload_entries} do {["image"], "", [entry]} -> %{data | "name" => entry.client_name} _ -> data end changeset = data |> changeset() |> Map.replace!(:action, :validate) {:noreply, assign(socket, changeset: changeset)} end def handle_event("clear_file", _params, socket) do {socket, _entries} = Phoenix.LiveView.Upload.maybe_cancel_uploads(socket) {:noreply, assign(socket, error_message: nil)} end def handle_event("save", %{"data" => data}, socket) do data |> changeset() |> apply_action(:insert) |> case do {:ok, data} -> %{files_dir: files_dir} = socket.assigns.session [upload_result] = consume_uploaded_entries(socket, :image, fn %{path: path}, _entry -> upload_file = FileSystem.File.local(path) destination_file = FileSystem.File.resolve(files_dir, data.name) result = with :ok <- FileSystem.File.copy(upload_file, destination_file) do {:ok, data.name} end {:ok, result} end) case upload_result do {:ok, filename} -> file_entry = %{name: filename, type: :attachment} Livebook.Session.add_file_entries(socket.assigns.session.pid, [file_entry]) url = "files/#{URI.encode(filename, &URI.char_unreserved?/1)}" send(self(), {:insert_image_complete, socket.assigns.insert_image_metadata, url}) {:noreply, push_patch(socket, to: socket.assigns.return_to)} {:error, message} -> {:noreply, assign(socket, error_message: message)} end {:error, changeset} -> {:noreply, assign(socket, changeset: changeset)} end end end