7

Hi I am trying to reimplement whatsapp functionality using elixir phoenix. I am having a problem figuring out the following: if all people in the chat room has received the message, I want to send the owner of the message status "received" so that he can show double tick sign. However how do you broadcast to one particular client?

almeynman
  • 7,088
  • 3
  • 23
  • 37
  • Check up this question: http://stackoverflow.com/questions/37373766/phoenix-return-ecto-query-results-to-a-specific-client/37375057#37375057 – simo Jun 08 '16 at 10:58
  • @simo The question is a bit off, because there client A sends the message and receives the message as well. What I need is: client A sends the message and only client B receives some feedback – almeynman Jun 08 '16 at 11:51
  • I think its a good idea to dedicate a topic for the client B only, then you can broadcast to that topic, otherwise I don't know how to exclude a specific user, have a look here: http://stackoverflow.com/questions/37279779/how-to-exclude-some-users-when-broadcast – simo Jun 08 '16 at 12:05
  • @simo I see. Dedicating seems quite complicated, especially in a group chat, where each person need his separate topic. I was just thinking that each socket has an id. and If I can contruct socket from id I can push messages at it. However I do not know how to do that – almeynman Jun 08 '16 at 12:21
  • Dedicating a topic for each user is easy, use the pattern matching for the topic name, as `def join("users:" <> user_id, _params, socket) do` – simo Jun 08 '16 at 12:34
  • @simo Actually I might go with your solution. I was just thinking that having a separate topic just for that small use case would be overkill. But I might need it anyways for other stuff. Thanks for help – almeynman Jun 08 '16 at 12:51
  • I don't know if having a topic per each user is would be overkill or not? I am not sure, although I am applying this in my project, after all, its a process for each user.. erlang will do the heavy lifting, but I didn't test that, I will test it using TSung tool if you know it, its very good to test with phoenix.. – simo Jun 08 '16 at 14:00
  • @simo never heard of it, but will give it a try :). You can write an answer about user specific topic and I will accept it. I actually needed user specific topics anyways, so it is natural fit in my case – almeynman Jun 09 '16 at 07:29

1 Answers1

13

You can solve this via topic per user, this can be easily implemented by pattern matching, notice the security validation also:

def join("users:" <> user_id, _params, socket) do
    {user_id, _} = Integer.parse(user_id)

    %{id: id} = socket.assigns[:user]

    #prevent connection to solo channel of other users, but allow in development
    case id == user_id || Mix.env == :dev do
      true ->
        {:ok, socket}
      false ->
        {:error, "This is not your solo channel!"}
      end
  end

As you would have stored the user from Repo.get when the user connect to the socket at:

defmodule MyApp.UserSocket do
  use Phoenix.Socket

  def connect(%{"token" => token}, socket) do
    case Phoenix.Token.verify(socket, "user", token, max_age: 1209600) do
      {:ok, user_id} ->
        socket = assign(socket, :user, Repo.get!(User, user_id))
        {:ok, socket}
      {:error, _} -> #...
    end
  end
end

And finally, you can send messages to a specific user out of socket context as:

YourAll.Endpoint.broadcast user_topic, "message", %{details: "etc"}

To test the performance, this is a very informative session in which Gary Rennie shows how to benchmark WebSockets using Tsung tool.

Ludovic Kuty
  • 4,868
  • 3
  • 28
  • 42
simo
  • 23,342
  • 38
  • 121
  • 218