2025-04-16 10:03:13 -03:00

94 lines
2.8 KiB
Elixir

defmodule Phoenix.LiveView.Route do
@moduledoc false
alias Phoenix.LiveView.{Route, Socket}
defstruct path: nil,
view: nil,
action: nil,
opts: [],
live_session: %{},
params: %{},
uri: nil
@doc """
Computes the container from the route options and falls backs to use options.
"""
def container(%Route{} = route) do
route.opts[:container] || route.view.__live__()[:container]
end
@doc """
Returns the internal or external matched LiveView route info for the given socket
and uri, raises if none is available.
"""
def live_link_info!(%Socket{router: nil}, view, _uri) do
raise ArgumentError,
"cannot invoke handle_params/3 on #{inspect(view)} " <>
"because it is not mounted nor accessed through the router live/3 macro"
end
def live_link_info!(%Socket{} = socket, view, uri) do
case live_link_info(socket.endpoint, socket.router, uri) do
{:internal, %Route{view: ^view} = route} ->
{:internal, route}
{:internal, %Route{view: _view} = route} ->
{:external, route.uri}
{:external, _parsed_uri} = external ->
external
:error ->
raise ArgumentError,
"cannot invoke handle_params nor live_redirect/live_patch to #{inspect(uri)} " <>
"because it isn't defined in #{inspect(socket.router)}"
end
end
@doc """
Returns the internal or external matched LiveView route info for the given uri.
"""
def live_link_info(endpoint, router, uri) when is_binary(uri) do
live_link_info(endpoint, router, URI.parse(uri))
end
def live_link_info(endpoint, router, %URI{} = parsed_uri)
when is_atom(endpoint) and is_atom(router) do
%URI{host: host, path: path, query: query} = parsed_uri
query_params = if query, do: Plug.Conn.Query.decode(query), else: %{}
split_path =
for segment <- String.split(path || "", "/"), segment != "", do: URI.decode(segment)
route_path = strip_segments(endpoint.script_name(), split_path) || split_path
case Phoenix.Router.route_info(router, "GET", route_path, host) do
%{plug: Phoenix.LiveView.Plug, phoenix_live_view: lv, path_params: path_params} ->
{view, action, opts, live_session} = lv
route = %Route{
view: view,
path: route_path,
action: action,
uri: parsed_uri,
opts: opts,
live_session: live_session,
params: Map.merge(query_params, path_params)
}
{:internal, route}
%{} ->
{:external, parsed_uri}
:error ->
:error
end
end
defp strip_segments([head | tail1], [head | tail2]), do: strip_segments(tail1, tail2)
defp strip_segments([], tail2), do: tail2
defp strip_segments(_, _), do: nil
end