Current section

Files

Jump to
phoenix_socket_client lib phoenix socket_client.ex
Raw

lib/phoenix/socket_client.ex

defmodule Phoenix.SocketClient do
@moduledoc """
The main API for the Phoenix Socket Client.
"""
@doc """
Starts the socket client supervisor.
Delegates to `Phoenix.SocketClient.Supervisor.start_link/1`.
"""
@spec start_link(keyword()) :: Supervisor.on_start()
defdelegate start_link(options), to: Phoenix.SocketClient.Supervisor
@doc """
Returns a child specification for the socket client supervisor.
Delegates to `Phoenix.SocketClient.Supervisor.child_spec/1`.
"""
@spec child_spec(keyword()) :: Supervisor.child_spec()
defdelegate child_spec(options), to: Phoenix.SocketClient.Supervisor
@doc """
Connects the socket.
"""
@spec connect(pid() | atom()) :: :ok | {:error, :no_socket_found}
def connect(sup_pid) do
case get_process_pid(sup_pid, :socket) do
nil ->
{:error, :no_socket_found}
socket_pid ->
send(socket_pid, :connect)
:ok
end
end
@doc false
def get_process_pid(sup_name, name) when is_atom(sup_name) do
case Process.whereis(sup_name) do
nil ->
nil
sup_pid ->
get_process_pid(sup_pid, name)
end
end
def get_process_pid(sup_pid, name) when is_pid(sup_pid) do
try do
case Process.alive?(sup_pid) do
false ->
nil
true ->
sup_pid
|> Supervisor.which_children()
|> Enum.find_value(fn
{process_name, process_pid, _, _} when is_pid(process_pid) and process_name == name ->
process_pid
_ ->
nil
end)
end
rescue
ArgumentError -> nil
_ -> nil
end
end
@doc """
Gets a value from the socket state.
"""
@spec get_state(pid() | atom(), atom()) :: any()
def get_state(name, key) when is_atom(name) do
case Process.whereis(name) do
nil ->
nil
sup_pid ->
get_state(sup_pid, key)
end
end
def get_state(pid, key) when is_pid(pid) do
state_pid = get_process_pid(pid, :socket_state)
Phoenix.SocketClient.Agent.get(state_pid, key)
end
@doc """
Gets the entire socket state.
"""
@spec get_state(pid() | atom()) :: map()
def get_state(name) when is_atom(name) do
case Process.whereis(name) do
nil ->
nil
sup_pid ->
get_state(sup_pid)
end
end
def get_state(pid) when is_pid(pid) do
state_pid = get_process_pid(pid, :socket_state)
Phoenix.SocketClient.Agent.get_state(state_pid)
end
@doc """
Puts a value into the socket state.
"""
@spec put_state(pid() | atom(), atom(), any()) :: :ok
def put_state(name, key, value) when is_atom(name) do
case Process.whereis(name) do
nil ->
nil
sup_pid ->
put_state(sup_pid, key, value)
end
end
def put_state(pid, key, value) when is_pid(pid) do
state_pid = get_process_pid(pid, :socket_state)
Phoenix.SocketClient.Agent.put(state_pid, key, value)
end
@doc """
Checks if the socket is connected.
"""
@spec connected?(pid | atom) :: boolean
def connected?(sup_pid) do
case get_process_pid(sup_pid, :socket) do
nil ->
false
pid ->
try do
GenServer.call(pid, :get_status, 5_000) == :connected
catch
:exit, _ -> false
end
end
end
@doc """
Joins a channel through the socket.
"""
@spec channel_join(pid, binary, map) :: {:ok, pid} | {:error, term}
def channel_join(sup_pid, topic, params \\ %{}) do
case get_process_pid(sup_pid, :channel_manager) do
nil ->
{:error, :channel_manager_not_found}
_manager_pid ->
Phoenix.SocketClient.ChannelManager.start_channel(sup_pid, topic, params)
end
end
@doc """
Leaves a channel.
"""
@spec channel_leave(pid, pid) :: :ok
def channel_leave(sup_pid, channel_pid) do
case get_process_pid(sup_pid, :channel_manager) do
nil -> :error
manager_pid -> DynamicSupervisor.terminate_child(manager_pid, channel_pid)
end
end
@doc """
Updates the status of a channel. For internal use.
"""
@spec update_channel_status(pid, pid, String.t(), atom(), map() | nil) :: :ok
def update_channel_status(sup_pid, channel_pid, topic, status, params \\ nil) do
state_pid = get_process_pid(sup_pid, :socket_state)
Phoenix.SocketClient.Agent.update_channel_status(
state_pid,
channel_pid,
topic,
status,
params
)
end
@doc """
Removes a channel from the list of joined channels. For internal use.
"""
@spec remove_channel(pid, String.t()) :: :ok
def remove_channel(sup_pid, topic) do
reconnecting = get_state(sup_pid, :reconnecting)
unless reconnecting do
state_pid = get_process_pid(sup_pid, :socket_state)
Phoenix.SocketClient.Agent.remove_channel(state_pid, topic)
end
end
@doc """
Pushes a message through the socket.
"""
@spec push(pid | atom, Phoenix.SocketClient.Message.t()) :: Phoenix.SocketClient.Message.t()
def push(sup_pid, message) do
case get_process_pid(sup_pid, :socket) do
nil ->
raise "Socket not found"
pid ->
GenServer.call(pid, {:push_message, message})
end
end
@doc """
Disconnects the socket.
"""
@spec disconnect(pid | atom) :: :ok
def disconnect(sup_pid) do
case get_process_pid(sup_pid, :socket) do
nil ->
:ok
socket_pid ->
put_state(sup_pid, :reconnect, false)
send(socket_pid, :disconnect)
:ok
end
end
end