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) # Only show text cells, as they have an actual content bin_entries = Enum.filter(bin_entries, fn entry -> Cell.type(entry.cell) in [:markdown, :elixir] end) {:ok, socket |> assign(:bin_entries, bin_entries) |> assign(assigns) |> assign_matching_entries()} end @impl true def render(assigns) do ~L"""

Bin

Here you can find all the deleted cells from this notebook session.

<%= if @bin_entries == [] do %>
<%= remix_icon("windy-line", class: "text-gray-400 text-xl") %>
There are currently no cells in the bin.
<% else %>
<%= for {%{cell: cell} = entry, index} <- Enum.take(@matching_entries, @limit) |> Enum.with_index(1) do %>

<%= index %>. <%= Cell.type(cell) |> Atom.to_string() |> String.capitalize() %> cell deleted from “<%= entry.section_name %>” section <%= entry.deleted_at |> DateTime.to_naive() |> Livebook.Utils.Time.time_ago_in_words() %> ago

<%= cell.source %>
<% end %> <%= if length(@matching_entries) > @limit do %>
<% end %>
<% end %>
""" 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_id, 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