2022-04-30 23:14:10 +08:00
|
|
|
defmodule LivebookWeb.SessionLive.PackageSearchLive do
|
2022-04-02 02:13:37 +08:00
|
|
|
use LivebookWeb, :live_view
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def mount(
|
|
|
|
_params,
|
|
|
|
%{"session" => session, "runtime" => runtime, "return_to" => return_to},
|
|
|
|
socket
|
|
|
|
) do
|
|
|
|
socket =
|
|
|
|
assign(socket,
|
|
|
|
session: session,
|
|
|
|
runtime: runtime,
|
|
|
|
return_to: return_to,
|
|
|
|
search: "",
|
|
|
|
search_ref: nil,
|
2022-04-30 23:14:10 +08:00
|
|
|
packages: [],
|
2022-04-02 02:13:37 +08:00
|
|
|
error_message: nil
|
|
|
|
)
|
|
|
|
|
|
|
|
socket = if connected?(socket), do: do_search(socket, ""), else: socket
|
|
|
|
|
|
|
|
{:ok, socket}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
|
|
|
~H"""
|
2022-09-01 05:53:23 +08:00
|
|
|
<div class="p-6 flex flex-col space-y-5">
|
2022-04-02 02:13:37 +08:00
|
|
|
<h3 class="text-2xl font-semibold text-gray-800">
|
2022-04-30 23:14:10 +08:00
|
|
|
Search packages
|
2022-04-02 02:13:37 +08:00
|
|
|
</h3>
|
|
|
|
<p class="text-gray-700">
|
|
|
|
Find external packages for your notebook
|
|
|
|
</p>
|
|
|
|
<form phx-submit="submit" phx-change="search">
|
2022-08-02 21:51:02 +08:00
|
|
|
<input
|
|
|
|
class="input"
|
2022-04-02 02:13:37 +08:00
|
|
|
name="search"
|
|
|
|
value={@search}
|
|
|
|
phx-debounce="250"
|
|
|
|
placeholder="Search"
|
|
|
|
autocomplete="off"
|
|
|
|
spellcheck="false"
|
2022-08-02 21:51:02 +08:00
|
|
|
autofocus
|
|
|
|
/>
|
2022-04-02 02:13:37 +08:00
|
|
|
</form>
|
2022-08-02 21:51:02 +08:00
|
|
|
<div class={
|
|
|
|
"flex flex-col divide-y h-[20rem] pr-2 -mr-2 overflow-y-auto tiny-scrollbar #{if @search_ref, do: "opacity-75"}"
|
|
|
|
}>
|
2022-04-02 02:13:37 +08:00
|
|
|
<%= cond do %>
|
|
|
|
<% @error_message -> %>
|
|
|
|
<div class="error-box">
|
|
|
|
<%= @error_message %>
|
|
|
|
</div>
|
2022-04-30 23:14:10 +08:00
|
|
|
<% @packages == [] -> %>
|
2022-08-02 21:51:02 +08:00
|
|
|
<div class="flex h-full items-center justify-center text-gray-600">
|
|
|
|
<.remix_icon icon="windy-line" class="text-xl" />
|
|
|
|
<div class="ml-2">No results</div>
|
|
|
|
</div>
|
|
|
|
<% true -> %>
|
|
|
|
<%= for {package, idx} <- Enum.with_index(@packages) do %>
|
|
|
|
<.package package={package} idx={idx} />
|
|
|
|
<% end %>
|
2022-04-02 02:13:37 +08:00
|
|
|
<% end %>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-04-30 23:14:10 +08:00
|
|
|
defp package(assigns) do
|
2022-04-02 02:13:37 +08:00
|
|
|
~H"""
|
|
|
|
<div class="flex items-center">
|
|
|
|
<div class="flex-grow p-2 flex flex-col text-sm">
|
|
|
|
<div class="flex text-gray-700">
|
2022-04-30 23:14:10 +08:00
|
|
|
<%= if @package[:url] do %>
|
|
|
|
<a class="font-semibold" href={@package[:url]} target="_blank"><%= @package.name %></a>
|
2022-04-02 02:13:37 +08:00
|
|
|
<% else %>
|
2022-04-30 23:14:10 +08:00
|
|
|
<span class="font-semibold"><%= @package.name %></span>
|
2022-04-02 02:13:37 +08:00
|
|
|
<% end %>
|
2022-04-30 23:14:10 +08:00
|
|
|
<span class="ml-1"><%= @package.version %></span>
|
2022-04-02 02:13:37 +08:00
|
|
|
</div>
|
|
|
|
<div class="text-gray-600">
|
2022-04-30 23:14:10 +08:00
|
|
|
<%= @package.description %>
|
2022-04-02 02:13:37 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="ml-2">
|
2022-08-02 21:51:02 +08:00
|
|
|
<button
|
|
|
|
class="button-base button-gray whitespace-nowrap py-1 px-2"
|
2022-04-02 02:13:37 +08:00
|
|
|
aria-label="add"
|
2022-08-02 21:51:02 +08:00
|
|
|
phx-click={JS.push("add", value: %{idx: @idx})}
|
|
|
|
>
|
2022-04-02 02:13:37 +08:00
|
|
|
<.remix_icon icon="add-line" class="align-middle mr-1 text-xs" />
|
|
|
|
<span class="font-normal text-xs">Add</span>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_event("search", %{"search" => search}, socket) do
|
|
|
|
{:noreply, do_search(socket, search)}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_event("submit", %{}, socket) do
|
|
|
|
socket =
|
2022-04-30 23:14:10 +08:00
|
|
|
case socket.assigns.packages do
|
2022-04-02 02:13:37 +08:00
|
|
|
[] -> socket
|
|
|
|
[first | _] -> add_dependency(socket, first.dependency)
|
|
|
|
end
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_event("add", %{"idx" => idx}, socket) do
|
2022-04-30 23:14:10 +08:00
|
|
|
package = Enum.fetch!(socket.assigns.packages, idx)
|
|
|
|
socket = add_dependency(socket, package.dependency)
|
2022-04-02 02:13:37 +08:00
|
|
|
{:noreply, socket}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_info(
|
2022-04-30 23:14:10 +08:00
|
|
|
{:runtime_search_packages_response, ref, response},
|
2022-04-02 02:13:37 +08:00
|
|
|
%{assigns: %{search_ref: ref}} = socket
|
|
|
|
) do
|
|
|
|
socket =
|
|
|
|
case response do
|
2022-04-30 23:14:10 +08:00
|
|
|
{:ok, packages} ->
|
|
|
|
assign(socket, packages: packages, error_message: nil)
|
2022-04-02 02:13:37 +08:00
|
|
|
|
|
|
|
{:error, message} ->
|
|
|
|
assign(socket, error_message: Livebook.Utils.upcase_first(message))
|
|
|
|
end
|
|
|
|
|
|
|
|
{:noreply, assign(socket, search_ref: nil)}
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_info(_message, socket), do: {:noreply, socket}
|
|
|
|
|
|
|
|
defp do_search(socket, search) do
|
2022-04-30 23:14:10 +08:00
|
|
|
search_ref = Livebook.Runtime.search_packages(socket.assigns.runtime, self(), search)
|
2022-04-02 02:13:37 +08:00
|
|
|
assign(socket, search_ref: search_ref, search: search)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp add_dependency(socket, dependency) do
|
|
|
|
Livebook.Session.add_dependencies(socket.assigns.session.pid, [dependency])
|
|
|
|
push_patch(socket, to: socket.assigns.return_to)
|
|
|
|
end
|
|
|
|
end
|