Как использовать Phoenix Presence для отслеживания наличия тем/подтем?

Я использую подтемы как однопользовательский канал, который другие могут использовать для отправки сообщений определенным людям.

Например: - Я пользователь 1, и я хочу отправить сообщение пользователю 2. - Я отправляю сообщение с полезной нагрузкой { to: 2, message: 'hi' }, а handle_in делает App.Endpoint.broadcast("user:2", "hi")

Вот фрагмент из моего user_channel.ex:

def handle_in("chat", incoming_payload, socket) do
  from_uid = socket.assigns.uid
  uid = incoming_payload["to"]
  message = incoming_payload["message"]
  topic = "user:#{uid}"
  payload = %{uid: from_uid, message: message}

  # Send to the topic based of the incoming_payload's 
  # 'to' param.
  App.Endpoint.broadcast(topic, "chat", payload)

  {:reply, :ok, socket}
end

Для всех, кому интересно: код с открытым исходным кодом

https://github.com/NullVoxPopuli/mesh-relay-phoenix/tree/feature/presence-tracking


person NullVoxPopuli    schedule 08.05.2017    source источник
comment
В чем вопрос?   -  person Alvin Lindstam    schedule 09.05.2017


Ответы (1)


Благодаря некоторым замечательным людям в слабом канале elixir-lang я смог прийти к этому решению (которое работает):

defmodule MeshRelay.UserChannel do
  use Phoenix.Channel, :channel
  alias MeshRelay.Presence
  require Logger

  defp uids_present(to_uid, from_uid) do
    to_uid && from_uid
  end

  defp has_valid_payload(payload) do
    uid = payload["to"]
    message = payload["message"]

    uid && message
  end

  # uid is the member's channel that
  # he/she receives their messages on.
  # no messages not intended to be received by
  # this member should be sent on this channel / subtopic
  #
  # socket.assigns.uid is the uid from the connect
  def join("user:" <> uid, _params, socket) do
    has_uids = uids_present(uid, socket.assigns.uid)

    if has_uids do
      send(self(), :after_join)
      # Logger.debug Presence.list(socket)
      {:ok, socket}
    else
      # kick him out he is not allowed here
      {:error,
        %{reason: "in order to receive messages, you must join a channel using your own uid"},
        socket
      }
    end
  end

  def handle_in("chat", incoming_payload, socket) do
    if has_valid_payload(incoming_payload) do
      from_uid = socket.assigns.uid
      uid = incoming_payload["to"]
      message = incoming_payload["message"]
      topic = "user:#{uid}"
      payload = %{uid: from_uid, message: message}

      if is_member_online?(uid) do
        MeshRelay.Endpoint.broadcast(topic, "chat", payload)
        # broadcast! socket, "chat", payload
        {:reply, :ok, socket}
      else
        reply_with_error_message(socket, %{
          reason: "member not found",
          to_uid: uid,
          from_uid: from_uid
        })
      end
    else
      reply_with_error_message(socket, %{
        reason: "please format your message: { \"to\": \"uidstring\", \"message\": \"encrypted message\" }"
      })
    end
  end

  def reply_with_error_message(socket, error) do
    {:reply, {:error, error}, socket }
  end

  def handle_info(:after_join, socket) do
    Presence.track(socket.channel_pid, "connected_members", socket.assigns.uid, %{
      online_at: inspect(System.system_time(:milli_seconds))
    })

    {:noreply, socket}
  end

  def is_member_online?(uid) do
    Presence.list("connected_members")
    |> Map.keys
    |> Enum.any?(fn key -> key == uid end)
  end


end
person NullVoxPopuli    schedule 09.05.2017