Packages
livebook
0.16.2
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