defmodule LivebookWeb.SessionLive.AppInfoComponent do
use LivebookWeb, :live_component
import LivebookWeb.AppHelpers
alias Livebook.Notebook.AppSettings
@impl true
def mount(socket) do
{:ok, assign(socket, deploy_confirmation: false)}
end
@impl true
def update(assigns, socket) do
changeset =
case socket.assigns do
%{changeset: changeset} when changeset.data == assigns.settings -> changeset
_ -> AppSettings.change(assigns.settings)
end
{:ok,
socket
|> assign(assigns)
|> assign(changeset: changeset)}
end
@impl true
def render(assigns) do
~H"""
App settings
<.app_info_icon />
<.app_form
changeset={@changeset}
deploy_confirmation={@deploy_confirmation}
session={@session}
myself={@myself}
/>
<%= if @apps != [] do %>
Deployments
<% end %>
"""
end
defp app_info_icon(assigns) do
~H"""
<.remix_icon icon="question-line" class="text-xl leading-none" />
"""
end
defp app_form(%{session: %{app_info: %{}}} = assigns) do
~H"""
This session is a running app. To deploy a modified version, you can fork it.
<.remix_icon icon="git-branch-line" />
Fork
"""
end
defp app_form(%{deploy_confirmation: true} = assigns) do
~H"""
Another app is already running under this slug, do you want to replace it?
Replace
Cancel
"""
end
defp app_form(assigns) do
~H"""
<.form
:let={f}
for={@changeset}
phx-submit="deploy"
phx-change="validate"
phx-target={@myself}
autocomplete="off"
>
<.text_field
field={f[:slug]}
label="Slug"
spellcheck="false"
phx-debounce
class="bg-gray-100"
/>
<.checkbox_field
field={f[:access_type]}
label="Password-protected"
checked_value="protected"
unchecked_value="public"
/>
<%= if Ecto.Changeset.get_field(@changeset, :access_type) == :protected do %>
<.password_field field={f[:password]} spellcheck="false" phx-debounce class="bg-gray-100" />
<% end %>
<.checkbox_field field={f[:show_source]} label="Show source" />
Deploy
Reset
"""
end
@impl true
def handle_event("validate", %{"_target" => ["reset"]}, socket) do
settings = AppSettings.new()
Livebook.Session.set_app_settings(socket.assigns.session.pid, settings)
{:noreply, assign(socket, changeset: AppSettings.change(settings))}
end
def handle_event("validate", %{"app_settings" => params}, socket) do
changeset =
socket.assigns.settings
|> AppSettings.change(params)
|> Map.put(:action, :validate)
with {:ok, settings} <- AppSettings.update(socket.assigns.settings, params) do
Livebook.Session.set_app_settings(socket.assigns.session.pid, settings)
end
{:noreply, assign(socket, changeset: changeset)}
end
def handle_event("deploy", %{"app_settings" => params}, socket) do
case AppSettings.update(socket.assigns.settings, params) do
{:ok, settings} ->
Livebook.Session.set_app_settings(socket.assigns.session.pid, settings)
if slug_taken?(settings.slug, socket.assigns.apps) do
{:noreply, assign(socket, deploy_confirmation: true)}
else
Livebook.Session.deploy_app(socket.assigns.session.pid)
{:noreply, socket}
end
{:error, _changeset} ->
{:noreply, socket}
end
end
def handle_event("deploy_confirmation_confirm", %{}, socket) do
Livebook.Session.deploy_app(socket.assigns.session.pid)
{:noreply, assign(socket, deploy_confirmation: false)}
end
def handle_event("deploy_confirmation_cancel", %{}, socket) do
{:noreply, assign(socket, deploy_confirmation: false)}
end
def handle_event("terminate_app", %{"session_id" => session_id}, socket) do
app = Enum.find(socket.assigns.apps, &(&1.session_id == session_id))
Livebook.Session.close(app.session_pid)
{:noreply, socket}
end
def handle_event("stop_app", %{"session_id" => session_id}, socket) do
app = Enum.find(socket.assigns.apps, &(&1.session_id == session_id))
Livebook.Session.app_stop(app.session_pid)
{:noreply, socket}
end
defp slug_taken?(slug, apps) do
own? =
Enum.any?(apps, fn app ->
app.registered and app.settings.slug == slug
end)
not own? and Livebook.Apps.exists?(slug)
end
end