Make the app list on auth screen live (#1836)

This commit is contained in:
Jonatan Kłosko 2023-03-29 00:02:07 +01:00 committed by GitHub
parent dbab38a763
commit 92bb567645
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 32 deletions

View file

@ -140,4 +140,17 @@ defmodule Livebook.Apps do
:ok
end
@doc """
Checks if the apps directory is configured and contains no notebooks.
"""
@spec empty_apps_path?() :: boolean()
def empty_apps_path?() do
if path = Livebook.Config.apps_path() do
pattern = Path.join([path, "**", "*.livemd"])
Path.wildcard(pattern) == []
else
false
end
end
end

View file

@ -25,8 +25,8 @@ defmodule LivebookWeb.AuthController do
render(conn, "index.html",
errors: [],
auth_mode: Livebook.Config.auth_mode(),
app_sessions: app_sessions(),
empty_apps_path?: empty_apps_path?()
any_public_app?: any_public_app?(),
empty_apps_path?: Livebook.Apps.empty_apps_path?()
)
end
@ -56,8 +56,8 @@ defmodule LivebookWeb.AuthController do
render(conn, "index.html",
errors: errors,
auth_mode: auth_mode,
app_sessions: app_sessions(),
empty_apps_path?: empty_apps_path?()
any_public_app?: any_public_app?(),
empty_apps_path?: Livebook.Apps.empty_apps_path?()
)
end
@ -75,18 +75,8 @@ defmodule LivebookWeb.AuthController do
|> halt()
end
defp app_sessions() do
defp any_public_app?() do
Livebook.Sessions.list_sessions()
|> Enum.filter(&(&1.mode == :app and &1.app_info.public? and &1.app_info.registered))
|> Enum.sort_by(& &1.notebook_name)
end
defp empty_apps_path?() do
if path = Livebook.Config.apps_path() do
pattern = Path.join([path, "**", "*.livemd"])
Path.wildcard(pattern) == []
else
false
end
|> Enum.any?(&(&1.mode == :app and &1.app_info.public?))
end
end

View file

@ -62,22 +62,15 @@
</div>
<div
:if={@app_sessions != [] or @empty_apps_path?}
:if={@any_public_app? or @empty_apps_path?}
class="w-full h-full px-4 py-8 flex justify-center items-center"
>
<div class="w-full flex flex-col items-center">
<div class="text-gray-700 text-xl font-medium">
Public apps
</div>
<div :if={@app_sessions != []} class="mt-5 max-w-[400px] w-full flex flex-col space-y-4">
<.link
:for={session <- @app_sessions}
navigate={~p"/apps/#{session.app_info.slug}"}
class="px-4 py-3 border border-gray-200 rounded-xl text-gray-800 pointer hover:bg-gray-50 flex justify-between"
>
<span class="font-semibold"><%= session.notebook_name %></span>
<.remix_icon icon="arrow-right-line" class="" />
</.link>
<div :if={@any_public_app?} class="w-full mt-5 mx-auto max-w-[400px]">
<%= live_render(@conn, LivebookWeb.AuthAppListLive) %>
</div>
<div :if={@empty_apps_path?} class="mt-5 text-gray-600">
<div>

View file

@ -5,41 +5,42 @@ defmodule LivebookWeb.AppHelpers do
Renders app status with indicator.
"""
attr :status, :atom, required: true
attr :show_label, :boolean, default: true
def app_status(%{status: :booting} = assigns) do
~H"""
<.app_status_indicator text="Booting" variant={:progressing} />
<.app_status_indicator text={@show_label && "Booting"} variant={:progressing} />
"""
end
def app_status(%{status: :running} = assigns) do
~H"""
<.app_status_indicator text="Running" variant={:success} />
<.app_status_indicator text={@show_label && "Running"} variant={:success} />
"""
end
def app_status(%{status: :error} = assigns) do
~H"""
<.app_status_indicator text="Error" variant={:error} />
<.app_status_indicator text={@show_label && "Error"} variant={:error} />
"""
end
def app_status(%{status: :shutting_down} = assigns) do
~H"""
<.app_status_indicator text="Shutting down" variant={:inactive} />
<.app_status_indicator text={@show_label && "Shutting down"} variant={:inactive} />
"""
end
def app_status(%{status: :stopped} = assigns) do
~H"""
<.app_status_indicator text="Stopped" variant={:inactive} />
<.app_status_indicator text={@show_label && "Stopped"} variant={:inactive} />
"""
end
defp app_status_indicator(assigns) do
~H"""
<div class="flex items-center space-x-2">
<div><%= @text %></div>
<div :if={@text}><%= @text %></div>
<.status_indicator variant={@variant} />
</div>
"""

View file

@ -0,0 +1,56 @@
defmodule LivebookWeb.AuthAppListLive do
use LivebookWeb, :live_view
import LivebookWeb.AppHelpers
import LivebookWeb.SessionHelpers
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Livebook.Sessions.subscribe()
end
sessions = Livebook.Sessions.list_sessions() |> Enum.filter(&(&1.mode == :app))
{:ok, assign(socket, sessions: sessions), layout: false}
end
@impl true
def render(assigns) do
~H"""
<div class="w-full flex flex-col space-y-4">
<.link
:for={session <- visible_sessions(@sessions)}
navigate={~p"/apps/#{session.app_info.slug}"}
class={[
"px-4 py-3 border border-gray-200 rounded-xl text-gray-800 pointer hover:bg-gray-50 flex justify-between",
not session.app_info.registered && "pointer-events-none"
]}
>
<span class="font-semibold"><%= session.notebook_name %></span>
<%= if session.app_info.registered do %>
<.remix_icon icon="arrow-right-line" class="" />
<% else %>
<div class="mr-0.5 flex">
<.app_status status={session.app_info.status} show_label={false} />
</div>
<% end %>
</.link>
</div>
"""
end
@impl true
def handle_info({type, session} = event, socket)
when type in [:session_created, :session_updated, :session_closed] and session.mode == :app do
{:noreply, update(socket, :sessions, &update_session_list(&1, event))}
end
def handle_info(_message, socket), do: {:noreply, socket}
defp visible_sessions(sessions) do
sessions
|> Enum.filter(& &1.app_info.public?)
|> Enum.sort_by(& &1.notebook_name)
end
end