0

In the docs there is this example but it only works for disconnects https://hexdocs.pm/phoenix/Phoenix.Socket.html#module-examples

  use Phoenix.Socket

  channel "room:*", MyAppWeb.RoomChannel

  def connect(params, socket, _connect_info) do
    {:ok, assign(socket, :user_id, params["user_id"])}
  end

  def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end

# Disconnect all user's socket connections and their multiplexed channels
MyAppWeb.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})

I have tried that and it only works for disconnects.

I've read in places that you can create a topic specific for that user but I could use a practical example. I am having trouble grokking the complexity and could use a practical example.

Thank you.

I was thinking if you could get the underlying socket in the ets table you can just push/4 to that socket but I don't know how to do that.

bezzoon
  • 1,755
  • 4
  • 24
  • 52

1 Answers1

2

You need to actually subscribe to a topic in order to receive the broadcast.

The "disconnect" message is special because it doesn't require subscription.

Inside the Phoenix.Socket source code, there exists these three lines:

  def __info__(%Broadcast{event: "disconnect"}, state) do
    {:stop, {:shutdown, :disconnected}, state}
  end

and they are the reason the socket state changes when the "disconnect" broadcast is received.

The "fallback" function looks like this:

  def __info__(_, state) do
    {:ok, state}
  end

which means for any and all "other" topics, the socket state is unchanged.

You need to manually subscribe to "other" topics.

It should look something like this:

socket.endpoint.subscribe("some_topic:" <> socket.assigns.user_id)

Then you can broadcast messages like this:

MyAppWeb.Endpoint.broadcast("some_topic:" <> user.id, "some_event", %{foo: bar})

If you are doing this with LiveView then the easiest way to get started is to put this in your mount function:

if connected?(socket) do
  socket.endpoint.subscribe("some_topic:" <> socket.assigns.user_id)
end

and to then have a handle_info like this:

  @impl true
  def handle_info(%{event: "some_event"} = info, socket) do
    IO.inspect info, label: "INFO"
    {:noreply, socket}
  end
Peaceful James
  • 1,807
  • 1
  • 7
  • 16
  • Ok so far so good (Also I am not using LV fwiw) but... def handle_info(%{event: event, payload: payload} = info, socket) do push(socket, event, payload) {:noreply, socket} end does not seem to be pushin an event to the front end. I assume that's how I am supposed to push the event to the FE? – bezzoon Apr 15 '21 at 12:30
  • Are you using a socket in your Javascript on the front end? There are good examples of using socket.js here: https://hexdocs.pm/phoenix/channels.html#using-token-authentication I think you should use `broadcast` to send events to Javascript. – Peaceful James Apr 15 '21 at 13:44
  • broadcast sends it to all the different users! and I am giving them unqiue user ids for sure – bezzoon Apr 15 '21 at 15:01
  • also yes I am using the socket.js provided by phoenix and handling other socket events – bezzoon Apr 15 '21 at 15:03
  • OK, I think you should not have the handle_info thing at all. You just need to connect to a channel in your javascript and then push to that. See these docs: https://hexdocs.pm/phoenix/js/ You only need to read the first few sections. Sorry I led you astray, I assumed you were using LV. – Peaceful James Apr 15 '21 at 17:11