defmodule LivebookWeb.SessionLive.BinComponent do use LivebookWeb, :live_component alias Livebook.Notebook.Cell @initial_limit 10 @limit_step 10 @impl true def mount(socket) do {:ok, assign(socket, search: "", limit: @initial_limit)} end @impl true def update(assigns, socket) do {bin_entries, assigns} = Map.pop(assigns, :bin_entries) {:ok, socket |> assign(:bin_entries, bin_entries) |> assign(assigns) |> assign_matching_entries()} end @impl true def render(assigns) do ~H"""

Bin

Find all your deleted cells from this notebook session

<%= if @bin_entries != [] do %>
<.remix_icon icon="search-line" class="align-bottom text-gray-500" />
<% end %>
<%= cond do %> <% @bin_entries == [] -> %>
<.remix_icon icon="delete-bin-6-line" class="text-gray-700 text-xl bg-gray-100 p-3 rounded-full" />
You haven't deleted any cells or sections yet. Once you do, they'll appear here.
<% @matching_entries == [] -> %>
<.remix_icon icon="file-search-line" class="text-gray-700 text-xl bg-gray-100 p-3 rounded-full" />
We couldn't find any results for your query.
<% true -> %>
<%= for %{cell: cell} = entry <- Enum.take(@matching_entries, @limit) do %>
<.cell_icon cell_type={Cell.type(cell)} />

<%= Cell.type(cell) |> Atom.to_string() |> String.capitalize() %> cell deleted from “<%= entry.section_name %>” section

<%= format_date_relatively(entry.deleted_at) %>
<.code_preview source_id={"bin-cell-#{cell.id}-source"} language={cell_language(cell)} source={cell.source} />
<% end %> <%= if length(@matching_entries) > @limit do %>
<% end %>
<% end %>
""" end defp cell_icon(%{cell_type: :code} = assigns) do ~H"""
""" end defp cell_icon(%{cell_type: :markdown} = assigns) do ~H"""
""" end defp cell_icon(%{cell_type: :smart} = assigns) do ~H"""
<.remix_icon icon="flashlight-line text-red-900" />
""" end defp cell_language(%Cell.Markdown{}), do: "markdown" defp cell_language(%Cell.Code{}), do: "elixir" defp cell_language(%Cell.Smart{}), do: "elixir" defp format_date_relatively(date) do time_words = date |> DateTime.to_naive() |> Livebook.Utils.Time.time_ago_in_words() time_words <> " ago" end @impl true def handle_event("search", %{"search" => search}, socket) do {:noreply, assign(socket, search: search, limit: @initial_limit) |> assign_matching_entries()} end def handle_event("more", %{}, socket) do {:noreply, assign(socket, limit: socket.assigns.limit + @limit_step)} end def handle_event("restore", %{"cell_id" => cell_id}, socket) do Livebook.Session.restore_cell(socket.assigns.session.pid, cell_id) {:noreply, push_patch(socket, to: socket.assigns.return_to)} end defp assign_matching_entries(socket) do matching_entries = filter_matching(socket.assigns.bin_entries, socket.assigns.search) assign(socket, matching_entries: matching_entries) end defp filter_matching(entries, search) do parts = search |> String.split() |> Enum.map(fn part -> ~r/#{Regex.escape(part)}/i end) Enum.filter(entries, fn entry -> Enum.all?(parts, fn part -> entry.cell.source =~ part end) end) end end