Phoenix Presence.update plays well with intercepted handle_out "presence_diff"?

Hi all.

Sorry for bringing your attention to this topic - I’m sure it’s been touched on before, but here goes … In my JavaScript Phoenix client, I’m running React hooks, and there is a component -a “Game Room Channel” context provider function- which talks to a Phoenix channel counterpart, which in turn has a Presence.track function and an intercepted handle_out “presence_diff” callback (On the client, there is a useEffect hook that sets up Presence.onJoin and Presence.onLeave listeners. Everything appeared to be going fine until I added a Presence.update function to the Phoenix channel, this in order to update a status field in the metadata. Now, the client is reporting what appears to be a “leave/join episode from another device” in the metas, although the new device appears to be identical with the current one. Checking the presences map on the server shows a pair of leave/joins for the same sub-key within each leaves/joins key respectively, (in the first metas list element, there is a phx_ref under the leaves sub-key that matches the phx_ref_prev under the joins key). I’ve been able to suppress these spurious leave/joins on the client, but I was wondering if this is normal Phoenix Presence behaviour, or if I’ve done something wrong?

1 Like

Can you explain more (and preferably show some code) for what you added here, as well as exactly what you were trying to accomplish?

Many thanks for your response. Apologies if not clear.

Basically, my “use case” was as follows:

In a basic game scenario there are 2 players. On the server there is a Phoenix channel process that accepts join requests to a game channel for 2 players maximum. Upon join there is an “after_join” handle info call which includes the following function to start tracking each player as follows:

defp track(user_name, player_type, socket) do
push(socket, “presence_state”, Presence.list(socket))
{:ok, _} =
Presence.track(
socket,
user_name,
# metadata map
%{
player_type: player_type,
status: “offline”
}
)
end

In the metadata I’m tracking an extra field called status. Actually, this field is not very important to either player, but only to other players who are looking for counterparts to play a game with. When 2 players join up to play a game both their statuses are set to “busy” and so are unavailable to other players.

In the same channel module I run the following to check if one player
leaves, and then use this information to decide if I have to update the
status of the other player.

intercept [“presence_diff”]

def handle_out(“presence_diff”, presences, socket) do
user = socket.assigns.user
leavers = presences.leaves |> Map.keys()

if leavers != [] do
  # There is a leaver, so, take the first one from the list ...
  [leaver|_] = leavers

  IO.puts "Player #{leaver} left the game."
  if user.player_type == :player1 do
     # run some code to update the other user status ...
  end
end
push(socket, "presence_diff", presences)
{:noreply, socket}

end

In the browser client there is a React function that has Phoenix
Presence.onJoin and Presence.onSync listener code initialized in a
useEffect section.

All this runs fine no problem.

The interesting thing was when I decided to add a Presence.update function to the Phoenix channel in an attempt to broadcast player status to other would-be players:

defp update(status, socket) do
user = socket.assigns.user
{:ok, _} =
Presence.update(
socket,
user.user_name,
# metadata map
%{
player_type: user.player_type,
status: status # this field is updated to a new value
}
)
end

This was then causing the handle_out presence_diff calback to fire, with what I can best describe as “join-leave” pairs for the same user key in leavers and joiners. This in turn was causing the client to report a false leave/join pair on the same device.

I can now see that I was miss-using the Phoenix Presence mechanism somewhat. I’ve found another way to broadcast player status to other users and it’s all working fine now.

I guess all I was trying to say was that when using Presence.update together with handle_out presence_diff callback, one has to be more careful since on has to separate this join-leave which is actually just an update from a genuine leave or join.