defmodule Phoenix.LiveView.Socket.AssignsNotInSocket do @moduledoc false defimpl Inspect do def inspect(_, _) do "#Phoenix.LiveView.Socket.AssignsNotInSocket<>" end end defstruct [:__assigns__] @type t :: %__MODULE__{} end defmodule Phoenix.LiveView.Socket do @moduledoc """ The LiveView socket for Phoenix Endpoints. This is typically mounted directly in your endpoint. socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] To share an underlying transport connection between regular Phoenix channels and LiveView processes, `use Phoenix.LiveView.Socket` from your own `MyAppWeb.UserSocket` module. Next, declare your `channel` definitions and optional `connect/3`, and `id/1` callbacks to handle your channel specific needs, then mount your own socket in your endpoint: socket "/live", MyAppWeb.UserSocket, websocket: [connect_info: [session: @session_options]] If you require session options to be set at runtime, you can use an MFA tuple. The function it designates must return a keyword list. socket "/live", MyAppWeb.UserSocket, websocket: [connect_info: [session: {__MODULE__, :runtime_opts, []}]] # ... def runtime_opts() do Keyword.put(@session_options, :domain, host()) end """ use Phoenix.Socket require Logger @derive {Inspect, only: [ :id, :endpoint, :router, :view, :parent_pid, :root_pid, :assigns, :transport_pid ]} defstruct id: nil, endpoint: nil, view: nil, parent_pid: nil, root_pid: nil, router: nil, assigns: %{__changed__: %{}}, private: %{live_temp: %{}}, fingerprints: Phoenix.LiveView.Diff.new_fingerprints(), redirected: nil, host_uri: nil, transport_pid: nil @typedoc "Struct returned when `assigns` is not in the socket." @opaque assigns_not_in_socket :: Phoenix.LiveView.Socket.AssignsNotInSocket.t() @typedoc "The data in a LiveView as stored in the socket." @type assigns :: map | assigns_not_in_socket() @type fingerprints :: {nil, map} | {binary, map} @type t :: %__MODULE__{ id: binary(), endpoint: module(), view: module(), parent_pid: nil | pid(), root_pid: pid(), router: module(), assigns: assigns, private: map(), fingerprints: fingerprints, redirected: nil | tuple(), host_uri: URI.t() | :not_mounted_at_router, transport_pid: pid() | nil } channel "lvu:*", Phoenix.LiveView.UploadChannel channel "lv:*", Phoenix.LiveView.Channel @impl Phoenix.Socket def connect(_params, %Phoenix.Socket{} = socket, connect_info) do {:ok, put_in(socket.private[:connect_info], connect_info)} end @impl Phoenix.Socket def id(socket), do: socket.private.connect_info[:session]["live_socket_id"] defmacro __using__(_opts) do quote do use Phoenix.Socket channel "lvu:*", Phoenix.LiveView.UploadChannel channel "lv:*", Phoenix.LiveView.Channel def connect(params, socket, info), do: {:ok, socket} defdelegate id(socket), to: unquote(__MODULE__) defoverridable connect: 3, id: 1 @before_compile unquote(__MODULE__) end end defmacro __before_compile__(_env) do quote do defoverridable connect: 3, id: 1 def connect(params, %Phoenix.Socket{} = socket, connect_info) do with {:ok, %Phoenix.Socket{} = new_socket} <- super(params, socket, connect_info) do Phoenix.LiveView.Socket.connect(params, new_socket, connect_info) end end end end end