0

クライアントが特定のチャネルに存在する必要なく、UserSocker使用中のユーザーの存在を追跡しようとしています。Phoenix.Presence

後で、ユーザーの存在について通知されるように、さまざまなチャネルでユーザーの存在を購読したいと思います。

presence_diffユーザーが切断したときに を受け取ることを除いて、これは機能しました。私がやっていることはUserSocket、各ユーザーの個別のトピックからプレゼンスを追跡することです:

defmodule MyAppWeb.UserSocket do

  # ...

  def connect(%{"user_id" => user_id, "password" => password}, socket) do
    case Accounts.authenticate(user_id, password) do
      {:ok, user} ->
        track_user_presence(socket.transport_pid, user)
        {:ok, assign(socket, :user, user)}
      _error -> :error
    end
  end

  defp user_presence_topic(user_id) do
    "user_presence:#{user_id}"
  end

  defp track_user_presence(pid, user) do
    MyApp.Presence.track(pid, user_presence_topic(user.id), user.id, %{
      online_at: inspect(System.system_time(:seconds))
    })
  end
end

私のチャンネルから、ユーザーの個別のプレゼンス トピックを購読しています。

defmodule MyAppWeb.RoomChannel do
  # ...

  def join("room:" <> room_id, payload, socket) do
    send(self(), :after_join)
    {:ok, assign(socket, :room_id, room_id)}
  end

  def handle_info(:after_join, socket) do
    user_ids = ~w(1 2)
    presence_state = get_and_subscribe_to_user_presence(socket, user_ids)
    push(socket, "presence_state", presence_state)
    {:noreply, socket}
  end

  def get_and_subscribe_to_user_presence(socket, user_ids) do
    user_ids
    |> Enum.map(&user_presence_topic/1)
    |> Enum.map(fn topic ->
      Phoenix.PubSub.subscribe(
        socket.pubsub_server,
        topic,
        fastlane: {socket.transport_pid, socket.serializer, []}
      )
      Presence.list(topic)
    end)
    |> Enum.reduce(%{}, fn map, acc -> Map.merge(acc, map) end)
  end

  defp user_presence_topic(user_id) do
    "user_presence:#{user_id}"
  end
end

transport_pidどうにかしてソケットを監視し、ソケットが終了したときにプレゼンス diff を自分で送信する必要があるという結論に達しました。

もう 1 つのアイデアは、クライアントを関数とは別のプレゼンス チャネルに参加させることUserSocket.connect/2でしたが、これまでのところ、それをアーカイブする方法がわかりませんでした。

問題の概要を説明するテストを使用して、単純なフェニックスアプリをハッキングしました: https://github.com/kbredemeier/socket_presence

これに関するアドバイスをいただければ幸いです。

4

1 に答える 1