Packages
livebook
0.14.0-rc.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/live/open_live/file_component.ex
defmodule LivebookWeb.OpenLive.FileComponent do
use LivebookWeb, :live_component
alias Livebook.LiveMarkdown
alias Livebook.FileSystem
@impl true
def mount(socket) do
{:ok, assign(socket, file_info: %{exists: true, access: :read_write})}
end
@impl true
def update(%{event: {:set_file, file, info}}, socket) do
file_info = %{exists: info.exists, access: file_access(file)}
{:ok, assign(socket, file: file, file_info: file_info)}
end
def update(assigns, socket) do
{initial_file, assigns} = Map.pop!(assigns, :initial_file)
{:ok,
socket
|> assign_new(:file, fn -> initial_file end)
|> assign(assigns)}
end
@impl true
def render(assigns) do
~H"""
<div class="h-80" role="region" aria-label="file storage">
<.live_component
module={LivebookWeb.FileSelectComponent}
id="home-file-select"
file={@file}
extnames={[LiveMarkdown.extension()]}
running_files={files(@sessions)}
target={{__MODULE__, @id}}
>
<div class="flex justify-end space-x-2">
<.button
color="gray"
outlined
phx-click="fork"
phx-target={@myself}
disabled={not path_forkable?(@file, @file_info)}
>
<.remix_icon icon="git-branch-line" />
<span>Fork</span>
</.button>
<%= if session = session_by_file(@file, @sessions) do %>
<.button navigate={~p"/sessions/#{session.id}"}>
Join session
</.button>
<% else %>
<span {open_button_tooltip_attrs(@file, @file_info)}>
<.button
phx-click="open"
phx-target={@myself}
disabled={not path_openable?(@file, @file_info, @sessions)}
>
Open
</.button>
</span>
<% end %>
</div>
</.live_component>
</div>
"""
end
defp open_button_tooltip_attrs(file, file_info) do
if regular?(file, file_info) and not writable?(file_info) do
[class: "tooltip top", "data-tooltip": "This file is write-protected, please fork instead"]
else
[]
end
end
@impl true
def handle_event("fork", %{}, socket) do
send(self(), {:fork, socket.assigns.file})
{:noreply, socket}
end
def handle_event("open", %{}, socket) do
send(self(), {:open, socket.assigns.file})
{:noreply, socket}
end
defp path_forkable?(file, file_info) do
regular?(file, file_info)
end
defp path_openable?(file, file_info, sessions) do
regular?(file, file_info) and not file_running?(file, sessions) and
writable?(file_info)
end
defp regular?(file, file_info) do
file_info.exists and not FileSystem.File.dir?(file)
end
defp writable?(file_info) do
file_info.access in [:read_write, :write]
end
defp file_access(file) do
case FileSystem.File.access(file) do
{:ok, access} -> access
{:error, _} -> :none
end
end
defp file_running?(file, sessions) do
Enum.any?(sessions, &(&1.file && FileSystem.File.equal?(&1.file, file)))
end
defp files(sessions) do
for session <- sessions, session.file, do: session.file
end
defp session_by_file(file, sessions) do
Enum.find(sessions, &(&1.file && FileSystem.File.equal?(&1.file, file)))
end
end