Current section
Files
Jump to
Current section
Files
lib/live_debugger/app/debugger/streams/web/streams_live.ex
defmodule LiveDebugger.App.Debugger.Streams.Web.StreamsLive do
@moduledoc """
This LiveView displays streams of a particular node (`LiveView` or `LiveComponent`).
It is meant to be used as a composable nested LiveView in the Debugger page.
"""
use LiveDebugger.App.Web, :live_view
alias LiveDebugger.Structs.LvProcess
alias LiveDebugger.App.Debugger.Streams.Web.Hooks
alias LiveDebugger.App.Debugger.Streams.Web.Components, as: StreamsComponents
alias LiveDebugger.Bus
alias LiveDebugger.Services.CallbackTracer.Events.StreamUpdated
alias LiveDebugger.App.Debugger.Events.NodeIdParamChanged
alias LiveDebugger.API.SettingsStorage
alias LiveDebugger.Client
@streams_section_id "streams-section-container"
@doc """
Renders the `StreamsLive` as a nested LiveView component.
`id` - dom id
`socket` - parent LiveView socket
`lv_process` - currently debugged LiveView process
`params` - query parameters of the page.
"""
attr(:id, :string, required: true)
attr(:socket, Phoenix.LiveView.Socket, required: true)
attr(:lv_process, LvProcess, required: true)
attr(:node_id, :any, required: true)
attr(:class, :string, default: "", doc: "CSS class for the container")
def live_render(assigns) do
session = %{
"lv_process" => assigns.lv_process,
"node_id" => assigns.node_id,
"parent_pid" => self()
}
assigns = assign(assigns, session: session)
~H"""
<%= live_render(@socket, __MODULE__,
id: @id,
session: @session,
container: {:div, class: @class}
) %>
"""
end
@impl true
def mount(_params, session, socket) do
lv_process = session["lv_process"]
parent_pid = session["parent_pid"]
node_id = session["node_id"]
if connected?(socket) do
Bus.receive_events!(parent_pid)
Bus.receive_states!(lv_process.pid)
end
socket
|> assign(:lv_process, lv_process)
|> assign(:node_id, node_id)
|> Hooks.Streams.init()
|> ok()
end
@impl true
def render(assigns) do
assigns = assign(assigns, id: @streams_section_id)
~H"""
<div class="flex-1 max-w-full flex flex-col gap-4">
<StreamsComponents.streams_section id={@id}>
<:display>
<.async_result :let={stream_names} assign={@stream_names}>
<:loading>
<StreamsComponents.loading />
</:loading>
<:failed>
<StreamsComponents.failed />
</:failed>
<%= if Enum.empty?(stream_names) do %>
<div class="w-full flex items-center justify-center">
<span class="text-secondary-text p-4">No streams found</span>
</div>
<% else %>
<StreamsComponents.streams_display_container
stream_names={stream_names}
existing_streams={@streams}
/>
<% end %>
</.async_result>
</:display>
</StreamsComponents.streams_section>
</div>
"""
end
@impl true
def handle_info(%NodeIdParamChanged{node_id: node_id}, socket) do
socket
|> assign(:node_id, node_id)
|> Hooks.Streams.assign_async_streams()
|> noreply()
end
@impl true
def handle_info(
%StreamUpdated{stream: stream, dom_id_fun: dom_id_fun},
socket
) do
socket
|> Hooks.Streams.assign_async_streams(stream, dom_id_fun)
|> push_event("#{@streams_section_id}-summary-pulse", %{})
|> noreply()
end
def handle_info(_, socket), do: {:noreply, socket}
@impl true
def handle_event("highlight", params, socket) do
socket
|> highlight_element(params)
|> noreply()
end
def handle_event("open-stream_element", %{"dom_id" => dom_id}, socket) do
socket
|> push_event(dom_id <> "-fullscreen-open", %{})
|> noreply()
end
defp highlight_element(%{assigns: %{lv_process: %LvProcess{alive?: true}}} = socket, params) do
if SettingsStorage.get(:highlight_in_browser) do
payload = %{
attr: params["search-attribute"],
val: params["search-value"],
type: params["type"],
id_value: params["id"]
}
Client.push_event!(socket.assigns.lv_process.root_socket_id, "highlight", payload)
end
socket
end
defp highlight_element(socket, _), do: socket
end