Phoenix.js Channel Presence onJoin firing multiple times

I’m trying to use a Phoenix Channel for the signaling part of a WebRTC application, as I thought Presence would be perfect for this.

The thing is: I’m trying to track a user join using Phoenix.js’ Presence object, like this:

  presence.onJoin(async (id, current, newPres) => {
    if (!current) {
      const peerConnection = await createPeerConnection(
      peerMap.set(id, peerConnection);
    } else {

The thing is: every time a user joins, this callback is fired multiple times, even with its own Presence object and the objects of users that were previously in the channel. I don’t know if this is the expected behavior and couldn’t find a reference about that.

My Channel looks like this right now:

defmodule ElmWebRtcWeb.VideoChannel do
  use Phoenix.Channel
  alias ElmWebrtcWeb.Presence

  def join("videoroom:" <> _channel, _message, socket) do
    send(self(), :after_join)
    {:ok, socket}

  def handle_info(:after_join, socket) do
    {:ok, _} =
      Presence.track(socket, socket.assigns.user_id, %{
        online_at: inspect(System.system_time(:millisecond))

    push(socket, "presence_state", Presence.list(socket))
    {:noreply, socket}

and the JS Presence is created like this:

  const channel =`videoroom:${room}`, {});
  const presence = new Presence(channel);

Does anyone have a clue about:

  • Is this the expected behavior of Presence.js?
  • If so, is there any way for me to achieve what I want with it?
  • If not, what could be going wrong?

Thank you very much!

1 Like

I haven’t been able to use this with const presence = Presence(channel), but this:

  let presences = {};

  const onJoin = async (id: string | undefined) => {
    //onJoin implementation

  const onLeave = async (id: string | undefined) => {
    //onLeave implementation

  channel.on('presence_diff', (diff) => {
    presences = Presence.syncDiff(presences, diff, onJoin, onLeave);

did the job and is working perfectly.

I’m still wondering if the first implementation should work though :thinking:

1 Like

I found the exact same thing when working with onJoin() - it would fire more than expected.

My workaround has been to use onSync() instead and then calling presence.list(), which is always (in my experience) accurate.

For example:

  presence.onSync(() => {
    const myParticipants = presence.list();
    // …  and so on

Hope that helps!