defmodule LivebookWeb.HomeLive do
use LivebookWeb, :live_view
import LivebookWeb.SessionHelpers
alias LivebookWeb.{LearnHelpers, LayoutHelpers}
alias Livebook.{Sessions, Notebook}
on_mount LivebookWeb.SidebarHook
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Livebook.Sessions.subscribe()
Livebook.SystemResources.subscribe()
Livebook.NotebookManager.subscribe_starred_notebooks()
end
sessions = Sessions.list_sessions() |> Enum.filter(&(&1.mode == :default))
notebook_infos = Notebook.Learn.visible_notebook_infos() |> Enum.take(3)
starred_notebooks = Livebook.NotebookManager.starred_notebooks()
{:ok,
assign(socket,
self_path: ~p"/",
sessions: sessions,
starred_notebooks: starred_notebooks,
starred_expanded?: false,
notebook_infos: notebook_infos,
page_title: "Livebook",
new_version: Livebook.UpdateCheck.new_version(),
update_instructions_url: Livebook.Config.update_instructions_url(),
app_service_url: Livebook.Config.app_service_url(),
memory: Livebook.SystemResources.memory()
)}
end
@impl true
def render(assigns) do
~H"""
<:topbar_action>
<.link navigate={~p"/open/file"} class="button-base button-outlined-gray whitespace-nowrap">
Open
<.link class="button-base button-blue" patch={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
New notebook
<.update_notification version={@new_version} instructions_url={@update_instructions_url} />
<.memory_notification memory={@memory} app_service_url={@app_service_url} />
<.link
navigate={~p"/open/file"}
class="button-base button-outlined-gray whitespace-nowrap"
>
Open
<.link class="button-base button-blue" patch={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
New notebook
Starred notebooks
6}
class="flex items-center text-blue-600"
phx-click="toggle_starred_expanded"
>
<%= if @starred_expanded? do %>
Show less
<% else %>
Show more
<% end %>
<%= if @starred_notebooks == [] do %>
<.no_entries>
Your starred notebooks will appear here.
First time around? Check out the notebooks below to get started.
<:actions>
<.link navigate={~p"/learn"} class="flex items-center text-blue-600 pl-5">
Learn more
<.remix_icon icon="arrow-right-line" class="align-middle ml-1" />
<% # Note: it's fine to use stateless components in this comprehension,
# because @notebook_infos never change %>
<% else %>
<.live_component
module={LivebookWeb.NotebookCardsComponent}
id="starred-notebook-list"
notebook_infos={visible_starred_notebooks(@starred_notebooks, @starred_expanded?)}
sessions={@sessions}
added_at_label="Starred"
>
<:card_icon :let={{_info, idx}}>
<.remix_icon icon="star-fill" class="text-yellow-600" />
<% end %>
<.live_component
module={LivebookWeb.HomeLive.SessionListComponent}
id="session-list"
sessions={@sessions}
starred_notebooks={@starred_notebooks}
memory={@memory}
/>
<.modal :if={@live_action == :import} id="import-modal" show width={:big} patch={@self_path}>
<.live_component
module={LivebookWeb.HomeLive.ImportComponent}
id="import"
tab={@tab}
import_opts={@import_opts}
/>
"""
end
defp update_notification(%{version: nil} = assigns), do: ~H""
defp update_notification(assigns) do
~H"""
Livebook v<%= @version %> available!
<%= if @instructions_url do %>
Check out the news on
livebook.dev
and follow the
update instructions
<% else %>
Check out the news and installation steps on
livebook.dev
<% end %>
🚀
"""
end
defp memory_notification(assigns) do
~H"""
<.remix_icon icon="alarm-warning-line" class="align-text-bottom mr-0.5" />
Less than 30 MB of memory left, consider
adding more resources to the instance
or closing
running sessions
"""
end
@impl true
def handle_params(%{"session_id" => session_id}, _url, socket) do
session = Enum.find(socket.assigns.sessions, &(&1.id == session_id))
{:noreply, assign(socket, session: session)}
end
def handle_params(%{}, _url, socket) when socket.assigns.live_action == :public_new_notebook do
{:noreply, create_session(socket, queue_setup: true)}
end
def handle_params(_params, _url, socket), do: {:noreply, socket}
@impl true
def handle_event("unstar_notebook", %{"idx" => idx}, socket) do
on_confirm = fn socket ->
%{file: file} = Enum.fetch!(socket.assigns.starred_notebooks, idx)
Livebook.NotebookManager.remove_starred_notebook(file)
socket
end
{:noreply,
confirm(socket, on_confirm,
title: "Unstar notebook",
description: "Once you unstar this notebook, you can always star it again.",
confirm_text: "Unstar",
opt_out_id: "unstar-notebook"
)}
end
def handle_event("toggle_starred_expanded", %{}, socket) do
{:noreply, update(socket, :starred_expanded?, ¬/1)}
end
@impl true
def handle_info({type, session} = event, socket)
when type in [:session_created, :session_updated, :session_closed] and
session.mode == :default do
{:noreply, update(socket, :sessions, &update_session_list(&1, event))}
end
def handle_info({:memory_update, memory}, socket) do
{:noreply, assign(socket, memory: memory)}
end
def handle_info({:starred_notebooks_updated, starred_notebooks}, socket) do
{:noreply, assign(socket, starred_notebooks: starred_notebooks)}
end
def handle_info({:fork, file}, socket) do
{:noreply, fork_notebook(socket, file)}
end
def handle_info({:open, file}, socket) do
{:noreply, open_notebook(socket, file)}
end
def handle_info(_message, socket), do: {:noreply, socket}
defp visible_starred_notebooks(notebooks, starred_expanded?)
defp visible_starred_notebooks(notebooks, true), do: notebooks
defp visible_starred_notebooks(notebooks, false), do: Enum.take(notebooks, 6)
end