From b1ce8748263f18b5d2a5585a6ecf216877e006ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Fri, 27 Oct 2023 20:49:46 +0200 Subject: [PATCH] Use start_async/3 for async file operations and migrate to phx-update="stream" (#2309) --- lib/livebook/notebook.ex | 2 +- lib/livebook_web/live/app_session_live.ex | 26 ++-- .../live/app_session_live/cell_outputs.ex | 43 +++++++ lib/livebook_web/live/output.ex | 119 +++++------------- .../live/output/frame_component.ex | 67 +++------- .../live/output/grid_component.ex | 51 ++++++++ .../live/output/markdown_component.ex | 22 ++-- .../live/output/plain_text_component.ex | 15 +-- .../live/output/tabs_component.ex | 77 ++++++++++++ .../live/output/terminal_text_component.ex | 32 +++-- lib/livebook_web/live/session_live.ex | 4 +- .../add_file_entry_file_component.ex | 38 +++--- .../add_file_entry_url_component.ex | 43 +++---- .../live/session_live/cell_component.ex | 38 +++++- .../live/session_live/files_list_component.ex | 56 ++++----- mix.exs | 3 +- mix.lock | 2 +- 17 files changed, 367 insertions(+), 271 deletions(-) create mode 100644 lib/livebook_web/live/app_session_live/cell_outputs.ex create mode 100644 lib/livebook_web/live/output/grid_component.ex create mode 100644 lib/livebook_web/live/output/tabs_component.ex diff --git a/lib/livebook/notebook.ex b/lib/livebook/notebook.ex index 64a9bc85d..eefec2297 100644 --- a/lib/livebook/notebook.ex +++ b/lib/livebook/notebook.ex @@ -867,7 +867,7 @@ defmodule Livebook.Notebook do do_prune_outputs(outputs, appendable?, acc) pruned_tabs_outputs -> - output = %{output | outputs: pruned_tabs_outputs, labels: :__pruned__} + output = %{output | outputs: pruned_tabs_outputs} do_prune_outputs(outputs, appendable?, [{idx, output} | acc]) end end diff --git a/lib/livebook_web/live/app_session_live.ex b/lib/livebook_web/live/app_session_live.ex index 1c0c17b14..46ec85445 100644 --- a/lib/livebook_web/live/app_session_live.ex +++ b/lib/livebook_web/live/app_session_live.ex @@ -140,22 +140,14 @@ defmodule LivebookWeb.AppSessionLive do
-
- -
+ module={LivebookWeb.AppSessionLive.CellOutputsComponent} + id={"outputs-#{cell_view.id}"} + cell_view={cell_view} + session={@session} + client_id={@client_id} + /> <%= if @data_view.app_status.execution == :error do %>
LivebookWeb.Output.MarkdownComponent end - send_update(module, id: "output-#{idx}", text: output.text) + send_update(module, id: "outputs-#{idx}-output", text: output.text) data_view _ -> diff --git a/lib/livebook_web/live/app_session_live/cell_outputs.ex b/lib/livebook_web/live/app_session_live/cell_outputs.ex new file mode 100644 index 000000000..88b2a79b9 --- /dev/null +++ b/lib/livebook_web/live/app_session_live/cell_outputs.ex @@ -0,0 +1,43 @@ +defmodule LivebookWeb.AppSessionLive.CellOutputsComponent do + use LivebookWeb, :live_component + + @impl true + def mount(socket) do + {:ok, stream(socket, :outputs, [])} + end + + @impl true + def update(assigns, socket) do + socket = assign(socket, assigns) + + stream_items = + for {idx, output} <- Enum.reverse(assigns.cell_view.outputs) do + %{id: Integer.to_string(idx), idx: idx, output: output} + end + + socket = stream(socket, :outputs, stream_items) + + {:ok, socket} + end + + @impl true + def render(assigns) do + ~H""" +
+ """ + end +end diff --git a/lib/livebook_web/live/output.ex b/lib/livebook_web/live/output.ex index e1f97cab7..5d411ca8e 100644 --- a/lib/livebook_web/live/output.ex +++ b/lib/livebook_web/live/output.ex @@ -6,27 +6,21 @@ defmodule LivebookWeb.Output do alias LivebookWeb.Output @doc """ - Renders a list of cell outputs. + Renders a single cell output. """ - attr :outputs, :list, required: true + attr :id, :string, required: true + attr :output, :map, required: true attr :session_id, :string, required: true attr :session_pid, :any, required: true attr :input_views, :map, required: true - attr :dom_id_map, :map, required: true attr :client_id, :string, required: true attr :cell_id, :string, required: true - def outputs(assigns) do + def output(assigns) do ~H""" -
- <%= render_output(output, %{ - id: "output-#{idx}", +
+ <%= render_output(@output, %{ + id: "#{@id}-output", session_id: @session_id, session_pid: @session_pid, input_views: @input_views, @@ -45,7 +39,6 @@ defmodule LivebookWeb.Output do defp render_output(%{type: :terminal_text, text: text}, %{id: id}) do text = if(text == :__pruned__, do: nil, else: text) - assigns = %{id: id, text: text} ~H""" @@ -55,7 +48,6 @@ defmodule LivebookWeb.Output do defp render_output(%{type: :plain_text, text: text}, %{id: id}) do text = if(text == :__pruned__, do: nil, else: text) - assigns = %{id: id, text: text} ~H""" @@ -65,7 +57,6 @@ defmodule LivebookWeb.Output do defp render_output(%{type: :markdown, text: text}, %{id: id, session_id: session_id}) do text = if(text == :__pruned__, do: nil, else: text) - assigns = %{id: id, session_id: session_id, text: text} ~H""" @@ -147,21 +138,8 @@ defmodule LivebookWeb.Output do client_id: client_id, cell_id: cell_id }) do - {labels, active_idx} = - if labels == :__pruned__ do - {[], nil} - else - labels = - Enum.zip_with(labels, outputs, fn label, {output_idx, _} -> {output_idx, label} end) - - active_idx = get_in(outputs, [Access.at(0), Access.elem(0)]) - - {labels, active_idx} - end - assigns = %{ id: id, - active_idx: active_idx, labels: labels, outputs: outputs, session_id: session_id, @@ -171,47 +149,18 @@ defmodule LivebookWeb.Output do cell_id: cell_id } - # After pruning we don't render labels and we render only those - # outputs that are kept during pruning - ~H""" -
-
- -
-
- <% # We use data-keep-attribute, because we know active_idx only on the first render %> -
- <.outputs - outputs={[{output_idx, output}]} - dom_id_map={%{}} - session_id={@session_id} - session_pid={@session_pid} - input_views={@input_views} - client_id={@client_id} - cell_id={@cell_id} - /> -
-
-
+ <.live_component + module={Output.TabsComponent} + id={@id} + outputs={@outputs} + labels={@labels} + session_id={@session_id} + session_pid={@session_pid} + input_views={@input_views} + client_id={@client_id} + cell_id={@cell_id} + /> """ end @@ -236,26 +185,18 @@ defmodule LivebookWeb.Output do } ~H""" -
-
-
- <.outputs - outputs={[{output_idx, output}]} - dom_id_map={%{}} - session_id={@session_id} - session_pid={@session_pid} - input_views={@input_views} - client_id={@client_id} - cell_id={@cell_id} - /> -
-
-
+ <.live_component + module={Output.GridComponent} + id={@id} + outputs={@outputs} + columns={@columns} + gap={@gap} + session_id={@session_id} + session_pid={@session_pid} + input_views={@input_views} + client_id={@client_id} + cell_id={@cell_id} + /> """ end diff --git a/lib/livebook_web/live/output/frame_component.ex b/lib/livebook_web/live/output/frame_component.ex index 23f85cfd6..291d834b5 100644 --- a/lib/livebook_web/live/output/frame_component.ex +++ b/lib/livebook_web/live/output/frame_component.ex @@ -3,8 +3,7 @@ defmodule LivebookWeb.Output.FrameComponent do @impl true def mount(socket) do - {:ok, assign(socket, counter: 0, output_count: 0, persistent_id_map: %{}), - temporary_assigns: [outputs: []]} + {:ok, stream(socket, :outputs, [])} end @impl true @@ -14,75 +13,47 @@ defmodule LivebookWeb.Output.FrameComponent do socket = assign(socket, assigns) - socket = - if socket.assigns.counter == 0 do - assign(socket, - counter: 1, - output_count: length(outputs), - persistent_id_map: map_idx_to_persistent_id(outputs, socket.assigns.id) - ) - else - socket - end + socket = assign_new(socket, :num_outputs, fn -> length(outputs) end) socket = case update_type do nil -> - assign(socket, outputs: outputs) + stream(socket, :outputs, stream_items(outputs)) :replace -> - prev_output_count = socket.assigns.output_count - prev_persistent_id_map = socket.assigns.persistent_id_map - - output_count = length(outputs) - persistent_id_map = map_idx_to_persistent_id(outputs, socket.assigns.id) - - socket = - assign(socket, - outputs: outputs, - output_count: output_count, - persistent_id_map: persistent_id_map - ) - - less_outputs? = prev_output_count > output_count - appended_outputs? = prev_output_count > map_size(prev_persistent_id_map) - - # If there are outputs that we need to remove, increase the counter. - # Otherwise we reuse DOM element ids via persistent_id_map - if less_outputs? or appended_outputs? do - update(socket, :counter, &(&1 + 1)) - else - socket - end + socket + |> assign(num_outputs: length(outputs)) + |> stream(:outputs, stream_items(outputs), reset: true) :append -> socket - |> assign(:outputs, outputs) - |> update(:output_count, &(length(outputs) + &1)) + |> update(:num_outputs, &(length(outputs) + &1)) + |> stream(:outputs, stream_items(outputs)) end {:ok, socket} end - defp map_idx_to_persistent_id(outputs, root_id) do - outputs - |> Enum.with_index() - |> Map.new(fn {{output_idx, _}, idx} -> {output_idx, "#{root_id}-#{idx}"} end) + defp stream_items(outputs) do + for {idx, output} <- Enum.reverse(outputs) do + %{id: Integer.to_string(idx), idx: idx, output: output} + end end @impl true def render(assigns) do ~H""" -
- <%= if @output_count == 0 do %> +
+ <%= if @num_outputs == 0 do %>
Nothing here...
<% else %> -
- + +
+
+ +
+
+
+ """ + end +end diff --git a/lib/livebook_web/live/output/markdown_component.ex b/lib/livebook_web/live/output/markdown_component.ex index 84b7ce68e..e2a09ee32 100644 --- a/lib/livebook_web/live/output/markdown_component.ex +++ b/lib/livebook_web/live/output/markdown_component.ex @@ -3,17 +3,21 @@ defmodule LivebookWeb.Output.MarkdownComponent do @impl true def mount(socket) do - {:ok, assign(socket, allowed_uri_schemes: Livebook.Config.allowed_uri_schemes(), chunks: 0), - temporary_assigns: [text: nil]} + {:ok, + socket + |> assign(allowed_uri_schemes: Livebook.Config.allowed_uri_schemes()) + |> stream(:chunks, [])} end @impl true def update(assigns, socket) do {text, assigns} = Map.pop(assigns, :text) + socket = assign(socket, assigns) if text do - {:ok, socket |> assign(text: text) |> update(:chunks, &(&1 + 1))} + chunk = %{id: Livebook.Utils.random_id(), text: text} + {:ok, stream_insert(socket, :chunks, chunk)} else {:ok, socket} end @@ -23,21 +27,19 @@ defmodule LivebookWeb.Output.MarkdownComponent do def render(assigns) do ~H"""
-
-
+ ><%= chunk.text %>
+
""" end diff --git a/lib/livebook_web/live/output/plain_text_component.ex b/lib/livebook_web/live/output/plain_text_component.ex index 820b2f3bc..5ffde08e6 100644 --- a/lib/livebook_web/live/output/plain_text_component.ex +++ b/lib/livebook_web/live/output/plain_text_component.ex @@ -3,16 +3,18 @@ defmodule LivebookWeb.Output.PlainTextComponent do @impl true def mount(socket) do - {:ok, assign(socket, chunks: 0), temporary_assigns: [text: nil]} + {:ok, stream(socket, :chunks, [])} end @impl true def update(assigns, socket) do {text, assigns} = Map.pop(assigns, :text) + socket = assign(socket, assigns) if text do - {:ok, socket |> assign(text: text) |> update(:chunks, &(&1 + 1))} + chunk = %{id: Livebook.Utils.random_id(), text: text} + {:ok, stream_insert(socket, :chunks, chunk)} else {:ok, socket} end @@ -21,13 +23,8 @@ defmodule LivebookWeb.Output.PlainTextComponent do @impl true def render(assigns) do ~H""" -
<%= - @text %>
+
<%= chunk.text %>
""" end end diff --git a/lib/livebook_web/live/output/tabs_component.ex b/lib/livebook_web/live/output/tabs_component.ex new file mode 100644 index 000000000..b2c2bd833 --- /dev/null +++ b/lib/livebook_web/live/output/tabs_component.ex @@ -0,0 +1,77 @@ +defmodule LivebookWeb.Output.TabsComponent do + use LivebookWeb, :live_component + + @impl true + def mount(socket) do + {:ok, stream(socket, :outputs, [])} + end + + @impl true + def update(assigns, socket) do + {labels, assigns} = Map.pop!(assigns, :labels) + {outputs, assigns} = Map.pop!(assigns, :outputs) + + # We compute these only on initial render, when we have all outputs + socket = + socket + |> assign_new(:labels, fn -> + Enum.zip_with(labels, outputs, fn label, {output_idx, _} -> {output_idx, label} end) + end) + |> assign_new(:active_idx, fn -> + get_in(outputs, [Access.at(0), Access.elem(0)]) + end) + + socket = assign(socket, assigns) + + stream_items = + for {idx, output} <- Enum.reverse(outputs) do + id = "#{socket.assigns.id}-tab-content-#{idx}" + %{id: id, idx: idx, output: output} + end + + socket = stream(socket, :outputs, stream_items) + + {:ok, socket} + end + + @impl true + def render(assigns) do + ~H""" +
+
+ +
+
+
+ +
+
+
+ """ + end +end diff --git a/lib/livebook_web/live/output/terminal_text_component.ex b/lib/livebook_web/live/output/terminal_text_component.ex index 0df544212..2a9b060a6 100644 --- a/lib/livebook_web/live/output/terminal_text_component.ex +++ b/lib/livebook_web/live/output/terminal_text_component.ex @@ -3,8 +3,10 @@ defmodule LivebookWeb.Output.TerminalTextComponent do @impl true def mount(socket) do - {:ok, assign(socket, modifiers: [], last_line: nil, last_html_line: nil), - temporary_assigns: [html_lines: []]} + {:ok, + socket + |> assign(modifiers: [], last_line: nil, last_html_line: nil) + |> stream(:html_lines, [])} end @impl true @@ -28,9 +30,13 @@ defmodule LivebookWeb.Output.TerminalTextComponent do {html_lines, [last_html_line]} = Enum.split(html_lines, -1) + stream_items = + for html_line <- html_lines, do: %{id: Livebook.Utils.random_id(), html: html_line} + + socket = stream(socket, :html_lines, stream_items) + {:ok, assign(socket, - html_lines: html_lines, last_html_line: last_html_line, last_line: last_line, modifiers: modifiers @@ -44,7 +50,7 @@ defmodule LivebookWeb.Output.TerminalTextComponent do def render(assigns) do ~H"""
<% # Note 1: We add a newline to each element, so that multiple lines can be copied properly as element.textContent %> <% # Note 2: We glue the tags together to avoid inserting unintended whitespace %> - +
@@ -72,7 +78,7 @@ defmodule LivebookWeb.Output.TerminalTextComponent do diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex index 408dca17f..b0da78505 100644 --- a/lib/livebook_web/live/session_live.ex +++ b/lib/livebook_web/live/session_live.ex @@ -2862,7 +2862,7 @@ defmodule LivebookWeb.SessionLive do for {{idx, frame}, cell} <- Notebook.find_frame_outputs(data.notebook, ref) do send_update(LivebookWeb.Output.FrameComponent, - id: "output-#{idx}", + id: "outputs-#{idx}-output", outputs: frame.outputs, update_type: update_type, # Note that we are not updating data_view to avoid re-render, @@ -2886,7 +2886,7 @@ defmodule LivebookWeb.SessionLive do :markdown -> LivebookWeb.Output.MarkdownComponent end - send_update(module, id: "output-#{idx}", text: output.text) + send_update(module, id: "outputs-#{idx}-output", text: output.text) data_view _ -> diff --git a/lib/livebook_web/live/session_live/add_file_entry_file_component.ex b/lib/livebook_web/live/session_live/add_file_entry_file_component.ex index 0385a131a..2055a5983 100644 --- a/lib/livebook_web/live/session_live/add_file_entry_file_component.ex +++ b/lib/livebook_web/live/session_live/add_file_entry_file_component.ex @@ -34,18 +34,6 @@ defmodule LivebookWeb.SessionLive.AddFileEntryFileComponent do {:ok, assign(socket, file: file, file_info: file_info, changeset: changeset)} end - def update(%{file_entry_result: file_entry_result}, socket) do - socket = assign(socket, fetching: false) - - case file_entry_result do - {:ok, file_entry} -> - {:ok, add_file_entry(socket, file_entry)} - - {:error, message} -> - {:ok, assign(socket, error_message: Livebook.Utils.upcase_first(message))} - end - end - def update(assigns, socket) do {:ok, socket @@ -125,7 +113,13 @@ defmodule LivebookWeb.SessionLive.AddFileEntryFileComponent do file_entry = %{name: data.name, type: :file, file: socket.assigns.file} if data.copy do - async_create_attachment_file_entry(socket, file_entry) + session = socket.assigns.session + + socket = + start_async(socket, :create_attachment_file_entry, fn -> + Livebook.Session.to_attachment_file_entry(session, file_entry) + end) + {:noreply, assign(socket, fetching: true, error_message: nil)} else {:noreply, add_file_entry(socket, file_entry)} @@ -136,15 +130,17 @@ defmodule LivebookWeb.SessionLive.AddFileEntryFileComponent do end end - defp async_create_attachment_file_entry(socket, file_entry) do - pid = self() - id = socket.assigns.id - session = socket.assigns.session + @impl true + def handle_async(:create_attachment_file_entry, {:ok, file_entry_result}, socket) do + socket = assign(socket, fetching: false) - Task.Supervisor.async_nolink(Livebook.TaskSupervisor, fn -> - file_entry_result = Livebook.Session.to_attachment_file_entry(session, file_entry) - send_update(pid, __MODULE__, id: id, file_entry_result: file_entry_result) - end) + case file_entry_result do + {:ok, file_entry} -> + {:noreply, add_file_entry(socket, file_entry)} + + {:error, message} -> + {:noreply, assign(socket, error_message: Livebook.Utils.upcase_first(message))} + end end defp add_file_entry(socket, file_entry) do diff --git a/lib/livebook_web/live/session_live/add_file_entry_url_component.ex b/lib/livebook_web/live/session_live/add_file_entry_url_component.ex index 3f1a79672..2b2e42e0c 100644 --- a/lib/livebook_web/live/session_live/add_file_entry_url_component.ex +++ b/lib/livebook_web/live/session_live/add_file_entry_url_component.ex @@ -8,23 +8,6 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUrlComponent do {:ok, assign(socket, changeset: changeset(), error_message: nil, fetching: false)} end - @impl true - def update(%{file_entry_result: file_entry_result}, socket) do - socket = assign(socket, fetching: false) - - case file_entry_result do - {:ok, file_entry} -> - {:ok, add_file_entry(socket, file_entry)} - - {:error, message} -> - {:ok, assign(socket, error_message: Livebook.Utils.upcase_first(message))} - end - end - - def update(assigns, socket) do - {:ok, assign(socket, assigns)} - end - defp changeset(attrs \\ %{}) do data = %{url: nil, name: nil, copy: false} types = %{url: :string, name: :string, copy: :boolean} @@ -137,7 +120,13 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUrlComponent do file_entry = %{name: data.name, type: :url, url: data.url} if data.copy do - async_create_attachment_file_entry(socket, file_entry) + session = socket.assigns.session + + socket = + start_async(socket, :file_entry, fn -> + Livebook.Session.to_attachment_file_entry(session, file_entry) + end) + {:noreply, assign(socket, fetching: true, error_message: nil)} else {:noreply, add_file_entry(socket, file_entry)} @@ -148,15 +137,17 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUrlComponent do end end - defp async_create_attachment_file_entry(socket, file_entry) do - pid = self() - id = socket.assigns.id - session = socket.assigns.session + @impl true + def handle_async(:file_entry, {:ok, file_entry_result}, socket) do + socket = assign(socket, fetching: false) - Task.Supervisor.async_nolink(Livebook.TaskSupervisor, fn -> - file_entry_result = Livebook.Session.to_attachment_file_entry(session, file_entry) - send_update(pid, __MODULE__, id: id, file_entry_result: file_entry_result) - end) + case file_entry_result do + {:ok, file_entry} -> + {:noreply, add_file_entry(socket, file_entry)} + + {:error, message} -> + {:noreply, assign(socket, error_message: Livebook.Utils.upcase_first(message))} + end end defp add_file_entry(socket, file_entry) do diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex index 124a2767e..ec03cd0a1 100644 --- a/lib/livebook_web/live/session_live/cell_component.ex +++ b/lib/livebook_web/live/session_live/cell_component.ex @@ -3,6 +3,32 @@ defmodule LivebookWeb.SessionLive.CellComponent do import LivebookWeb.SessionHelpers + @impl true + def mount(socket) do + {:ok, stream(socket, :outputs, [])} + end + + @impl true + def update(assigns, socket) do + socket = assign(socket, assigns) + + socket = + case assigns.cell_view do + %{eval: %{outputs: outputs}} -> + stream_items = + for {idx, output} <- Enum.reverse(outputs) do + %{id: Integer.to_string(idx), idx: idx, output: output} + end + + stream(socket, :outputs, stream_items) + + %{} -> + socket + end + + {:ok, socket} + end + @impl true def render(assigns) do ~H""" @@ -99,6 +125,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<.doctest_summary cell_id={@cell_view.id} doctest_summary={@cell_view.eval.doctest_summary} /> <.evaluation_outputs + outputs={@streams.outputs} cell_view={@cell_view} session_id={@session_id} session_pid={@session_pid} @@ -146,6 +173,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<.evaluation_outputs + outputs={@streams.outputs} cell_view={@cell_view} session_id={@session_id} session_pid={@session_pid} @@ -249,6 +277,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<.evaluation_outputs + outputs={@streams.outputs} cell_view={@cell_view} session_id={@session_id} session_pid={@session_pid} @@ -634,11 +663,12 @@ defmodule LivebookWeb.SessionLive.CellComponent do class="flex flex-col" data-el-outputs-container id={"outputs-#{@cell_view.id}-#{@cell_view.eval.outputs_batch_number}"} - phx-update="append" + phx-update="stream" > - - Livebook.Session.add_file_entries(socket.assigns.session.pid, [file_entry]) - - {:error, message} -> - send(self(), {:put_flash, :error, Livebook.Utils.upcase_first(message)}) - end - - socket = update(socket, :transferring_file_entry_names, &MapSet.delete(&1, name)) - - {:ok, socket} - end - def update(assigns, socket) do socket = assign(socket, assigns) @@ -326,22 +312,19 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do end def handle_event("transfer_file_entry", %{"name" => name}, socket) do - if file_entry = find_file_entry(socket, name) do - pid = self() - id = socket.assigns.id - session = socket.assigns.session + socket = + if file_entry = find_file_entry(socket, name) do + session = socket.assigns.session - Task.Supervisor.async_nolink(Livebook.TaskSupervisor, fn -> - file_entry_result = Livebook.Session.to_attachment_file_entry(session, file_entry) - - send_update(pid, __MODULE__, - id: id, - transfer_file_entry_result: {name, file_entry_result} - ) - end) - end - - socket = update(socket, :transferring_file_entry_names, &MapSet.put(&1, name)) + socket + |> start_async(:transfer_file_entry, fn -> + file_entry_result = Livebook.Session.to_attachment_file_entry(session, file_entry) + {name, file_entry_result} + end) + |> update(:transferring_file_entry_names, &MapSet.put(&1, name)) + else + socket + end {:noreply, socket} end @@ -354,6 +337,21 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do {:noreply, socket} end + @impl true + def handle_async(:transfer_file_entry, {:ok, {name, file_entry_result}}, socket) do + case file_entry_result do + {:ok, file_entry} -> + Livebook.Session.add_file_entries(socket.assigns.session.pid, [file_entry]) + + {:error, message} -> + send(self(), {:put_flash, :error, Livebook.Utils.upcase_first(message)}) + end + + socket = update(socket, :transferring_file_entry_names, &MapSet.delete(&1, name)) + + {:noreply, socket} + end + defp find_file_entry(socket, name) do Enum.find(socket.assigns.file_entries, &(&1.name == name)) end diff --git a/mix.exs b/mix.exs index 25d2e97c3..445cb8cdf 100644 --- a/mix.exs +++ b/mix.exs @@ -95,7 +95,8 @@ defmodule Livebook.MixProject do defp deps do [ {:phoenix, "~> 1.7.8"}, - {:phoenix_live_view, "~> 0.20.1"}, + # {:phoenix_live_view, "~> 0.20.1"}, + {:phoenix_live_view, github: "phoenixframework/phoenix_live_view", override: true}, {:phoenix_html, "~> 3.0"}, {:phoenix_live_dashboard, "~> 0.8.0"}, {:telemetry_metrics, "~> 0.4"}, diff --git a/mix.lock b/mix.lock index b9b2c21c9..54da68bc5 100644 --- a/mix.lock +++ b/mix.lock @@ -31,7 +31,7 @@ "phoenix_html": {:hex, :phoenix_html, "3.3.2", "d6ce982c6d8247d2fc0defe625255c721fb8d5f1942c5ac051f6177bffa5973f", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "44adaf8e667c1c20fb9d284b6b0fa8dc7946ce29e81ce621860aa7e96de9a11d"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.2", "b9e33c950d1ed98494bfbde1c34c6e51c8a4214f3bea3f07ca9a510643ee1387", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "67a598441b5f583d301a77e0298719f9654887d3d8bf14e80ff0b6acf887ef90"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.1", "92a37acf07afca67ac98bd326532ba8f44ad7d4bdf3e4361b03f7f02594e5ae9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be494fd1215052729298b0e97d5c2ce8e719c00854b82cd8cf15c1cd7fcf6294"}, + "phoenix_live_view": {:git, "https://github.com/phoenixframework/phoenix_live_view.git", "2df6832a4f93a730e47f25bb39a57b6714f9da32", []}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"},