2021-03-04 05:56:28 +08:00
|
|
|
defmodule LivebookWeb.SessionLive.PersistenceComponent do
|
|
|
|
use LivebookWeb, :live_component
|
2021-02-21 23:54:44 +08:00
|
|
|
|
2021-03-04 05:56:28 +08:00
|
|
|
alias Livebook.{Session, SessionSupervisor, LiveMarkdown}
|
2021-02-21 23:54:44 +08:00
|
|
|
|
|
|
|
@impl true
|
|
|
|
def mount(socket) do
|
|
|
|
session_summaries = SessionSupervisor.get_session_summaries()
|
2021-04-01 18:56:19 +08:00
|
|
|
running_paths = Enum.map(session_summaries, & &1.path)
|
|
|
|
{:ok, assign(socket, running_paths: running_paths)}
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
|
2021-07-30 22:24:46 +08:00
|
|
|
@impl true
|
|
|
|
def update(assigns, socket) do
|
|
|
|
{path, assigns} = Map.pop!(assigns, :path)
|
|
|
|
{persist_outputs, assigns} = Map.pop!(assigns, :persist_outputs)
|
|
|
|
|
|
|
|
attrs = %{path: path, persist_outputs: persist_outputs}
|
|
|
|
|
|
|
|
socket =
|
|
|
|
socket
|
|
|
|
|> assign(assigns)
|
|
|
|
|> assign(attrs: attrs, new_attrs: attrs)
|
|
|
|
|
|
|
|
{:ok, socket}
|
|
|
|
end
|
|
|
|
|
2021-02-21 23:54:44 +08:00
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
2021-07-07 20:32:49 +08:00
|
|
|
~H"""
|
2021-07-30 22:24:46 +08:00
|
|
|
<div class="p-6 pb-4 flex flex-col space-y-8">
|
2021-04-22 05:02:09 +08:00
|
|
|
<h3 class="text-2xl font-semibold text-gray-800">
|
|
|
|
File
|
|
|
|
</h3>
|
2021-07-30 22:24:46 +08:00
|
|
|
<div class="w-full flex-col space-y-8">
|
|
|
|
<div class="flex">
|
|
|
|
<form phx-change="set_options" onsubmit="return false;" phx-target={@myself}>
|
|
|
|
<.switch_checkbox
|
|
|
|
name="persist_outputs"
|
|
|
|
label="Persist outputs"
|
|
|
|
checked={@new_attrs.persist_outputs} />
|
|
|
|
</form>
|
|
|
|
</div>
|
2021-04-22 05:02:09 +08:00
|
|
|
<div class="flex space-x-4">
|
2021-07-07 20:32:49 +08:00
|
|
|
<.choice_button
|
2021-07-30 22:24:46 +08:00
|
|
|
active={@new_attrs.path != nil}
|
2021-07-07 20:32:49 +08:00
|
|
|
phx-click="set_persistence_type"
|
|
|
|
phx-value-type="file"
|
|
|
|
phx-target={@myself}>
|
|
|
|
Save to file
|
|
|
|
</.choice_button>
|
|
|
|
<.choice_button
|
2021-07-30 22:24:46 +08:00
|
|
|
active={@new_attrs.path == nil}
|
2021-07-07 20:32:49 +08:00
|
|
|
phx-click="set_persistence_type"
|
|
|
|
phx-value-type="memory"
|
|
|
|
phx-target={@myself}>
|
|
|
|
Memory only
|
|
|
|
</.choice_button>
|
2021-02-21 23:54:44 +08:00
|
|
|
</div>
|
2021-07-30 22:24:46 +08:00
|
|
|
<%= if @new_attrs.path != nil do %>
|
2021-04-22 05:02:09 +08:00
|
|
|
<div class="h-full h-52">
|
2021-06-03 19:53:03 +08:00
|
|
|
<%= live_component LivebookWeb.PathSelectComponent,
|
2021-07-07 20:32:49 +08:00
|
|
|
id: "path_select",
|
2021-07-30 22:24:46 +08:00
|
|
|
path: @new_attrs.path,
|
2021-07-07 20:32:49 +08:00
|
|
|
extnames: [LiveMarkdown.extension()],
|
|
|
|
running_paths: @running_paths,
|
|
|
|
phx_target: @myself,
|
2021-07-30 22:24:46 +08:00
|
|
|
phx_submit: if(disabled?(@new_attrs, @attrs, @running_paths), do: nil, else: "save") %>
|
2021-02-21 23:54:44 +08:00
|
|
|
</div>
|
|
|
|
<% end %>
|
2021-07-30 22:24:46 +08:00
|
|
|
<div class="flex flex-col space-y-8">
|
|
|
|
<%= if @new_attrs.path != nil do %>
|
2021-04-22 05:02:09 +08:00
|
|
|
<div class="text-gray-500 text-sm">
|
2021-07-30 22:24:46 +08:00
|
|
|
File: <%= normalize_path(@new_attrs.path) %>
|
2021-04-22 05:02:09 +08:00
|
|
|
</div>
|
|
|
|
<% end %>
|
|
|
|
<div>
|
2021-07-30 22:24:46 +08:00
|
|
|
<button class="button button-blue"
|
2021-07-07 20:32:49 +08:00
|
|
|
phx-click="save"
|
|
|
|
phx-target={@myself}
|
2021-07-30 22:24:46 +08:00
|
|
|
disabled={disabled?(@new_attrs, @attrs, @running_paths)}>
|
2021-07-07 20:32:49 +08:00
|
|
|
Save
|
|
|
|
</button>
|
2021-04-22 05:02:09 +08:00
|
|
|
</div>
|
2021-03-24 00:46:33 +08:00
|
|
|
</div>
|
2021-02-21 23:54:44 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_event("set_persistence_type", %{"type" => type}, socket) do
|
|
|
|
path =
|
|
|
|
case type do
|
2021-07-30 22:24:46 +08:00
|
|
|
"file" -> socket.assigns.attrs.path || default_path()
|
2021-02-21 23:54:44 +08:00
|
|
|
"memory" -> nil
|
|
|
|
end
|
|
|
|
|
2021-07-30 22:24:46 +08:00
|
|
|
{:noreply, put_new_attr(socket, :path, path)}
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def handle_event("set_path", %{"path" => path}, socket) do
|
2021-07-30 22:24:46 +08:00
|
|
|
{:noreply, put_new_attr(socket, :path, path)}
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_event("set_options", %{"persist_outputs" => persist_outputs}, socket) do
|
|
|
|
persist_outputs = persist_outputs == "true"
|
|
|
|
{:noreply, put_new_attr(socket, :persist_outputs, persist_outputs)}
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
|
2021-07-30 22:24:46 +08:00
|
|
|
def handle_event("save", %{}, %{assigns: assigns} = socket) do
|
|
|
|
path = normalize_path(assigns.new_attrs.path)
|
|
|
|
|
|
|
|
if path != assigns.attrs.path do
|
|
|
|
Session.set_path(assigns.session_id, path)
|
|
|
|
end
|
|
|
|
|
|
|
|
Session.set_notebook_attributes(assigns.session_id, %{
|
|
|
|
persist_outputs: assigns.new_attrs.persist_outputs
|
|
|
|
})
|
|
|
|
|
|
|
|
Session.save_sync(assigns.session_id)
|
2021-04-01 18:56:19 +08:00
|
|
|
|
|
|
|
running_paths =
|
|
|
|
if path do
|
2021-07-30 22:24:46 +08:00
|
|
|
[path | assigns.running_paths]
|
2021-04-01 18:56:19 +08:00
|
|
|
else
|
2021-07-30 22:24:46 +08:00
|
|
|
List.delete(assigns.running_paths, path)
|
2021-04-01 18:56:19 +08:00
|
|
|
end
|
|
|
|
|
2021-04-22 05:02:09 +08:00
|
|
|
# After saving the file reload the directory contents,
|
|
|
|
# so that the new file gets shown.
|
|
|
|
send_update(LivebookWeb.PathSelectComponent,
|
|
|
|
id: "path_select",
|
|
|
|
running_paths: running_paths,
|
|
|
|
force_reload: true
|
|
|
|
)
|
|
|
|
|
2021-04-01 18:56:19 +08:00
|
|
|
{:noreply, assign(socket, running_paths: running_paths)}
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
|
2021-07-30 22:24:46 +08:00
|
|
|
defp put_new_attr(socket, key, value) do
|
|
|
|
new_attrs = socket.assigns.new_attrs
|
|
|
|
new_attrs = put_in(new_attrs[key], value)
|
|
|
|
assign(socket, :new_attrs, new_attrs)
|
|
|
|
end
|
|
|
|
|
2021-02-21 23:54:44 +08:00
|
|
|
defp default_path() do
|
2021-04-15 20:51:27 +08:00
|
|
|
Livebook.Config.root_path() |> Path.join("notebook")
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
|
2021-04-01 18:56:19 +08:00
|
|
|
defp path_savable?(nil, _running_paths), do: true
|
2021-02-21 23:54:44 +08:00
|
|
|
|
2021-04-01 18:56:19 +08:00
|
|
|
defp path_savable?(path, running_paths) do
|
2021-02-21 23:54:44 +08:00
|
|
|
if File.exists?(path) do
|
|
|
|
File.regular?(path) and path not in running_paths
|
|
|
|
else
|
2021-04-01 18:56:19 +08:00
|
|
|
true
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp normalize_path(nil), do: nil
|
|
|
|
|
|
|
|
defp normalize_path(path) do
|
|
|
|
if String.ends_with?(path, LiveMarkdown.extension()) do
|
|
|
|
path
|
|
|
|
else
|
|
|
|
path <> LiveMarkdown.extension()
|
|
|
|
end
|
|
|
|
end
|
2021-04-13 04:59:48 +08:00
|
|
|
|
2021-07-30 22:24:46 +08:00
|
|
|
defp disabled?(new_attrs, attrs, running_paths) do
|
|
|
|
if normalize_path(new_attrs.path) == attrs.path do
|
|
|
|
new_attrs.persist_outputs == attrs.persist_outputs
|
|
|
|
else
|
|
|
|
not path_savable?(normalize_path(new_attrs.path), running_paths)
|
|
|
|
end
|
2021-04-13 04:59:48 +08:00
|
|
|
end
|
2021-02-21 23:54:44 +08:00
|
|
|
end
|