I’m working on Application which requires limiting users to 1 tab open at any time. In order to do this, I’m using Presence to track and untrack to know if they are connected to the Channel. Here is how I’m untracking on termination:
def terminate(_reason, %{assigns: %{current_user: user}} = socket) do
:ok = Presence.untrack(socket, user.id)
end
And when the user joins the channel, I check if they’re already tracked. If they are, I reply with an error and then redirect them client side:
def join("room:" <> id, _payload, socket) do
cond do
already_in_room?(socket) ->
{:error, %{reason: "already_in_room"}}
true ->
...
end
end
...
defp already_in_room?(%{assigns: %{current_user: user}} = socket) do
socket
|> Presence.list()
|> Map.has_key?(user.id)
end
The client error handler:
const handleRoomChannelError = (resp) => {
console.log("Unable to join room channel", resp);
if (resp.reason === "already_in_room") {
window.location.href = `/rooms/already_in?return_to=${window.location.pathname}`;
}
};
This is all mostly working fine, except that sometimes I’m seeing users in 1 tab suddenly become redirected. I’m guessing that their browser is reconnecting without terminating properly beforehand.
This requirement comes from dealing with a third party API which errors if we connect from multiple tabs, so not doing this isn’t an option.
My questions then are:
- Am I untracking the Presence or handling the termination correctly? If not, what should I be doing?
- Am I misusing Presence for this? Is relying on the termination of the connection to the Channel for a feature a bad idea?
- If it is, any suggestions of how to handle this another way?
Thanks