So I have a LiveView that I monitor using the following code:
defmodule IntisyncWeb.LiveViewMonitor do
@moduledoc """
Monitors LiveView processes and calls their `unmount` functions when they die
"""
use GenServer
def start_link(init_arg) do
GenServer.start_link(__MODULE__, init_arg, name: {:global, __MODULE__})
end
def init(_) do
{:ok, %{views: %{}}}
end
def monitor(pid, view_module, meta) do
server_pid = GenServer.whereis({:global, __MODULE__})
GenServer.call(server_pid, {:monitor, pid, view_module, meta})
end
def handle_call({:monitor, pid, view_module, meta}, _, %{views: views} = state) do
_ref = Process.monitor(pid)
{:reply, :ok, %{state | views: Map.put(views, pid, {view_module, meta})}}
end
def handle_info({:DOWN, ref, :process, pid, reason}, state) do
{{module, meta}, new_views} = Map.pop(state.views, pid)
Process.demonitor(ref)
Task.start(fn -> module.unmount(reason, meta) end)
{:noreply, %{state | views: new_views}}
end
end
Then, on the LiveView I do the following:
defmodule IntisyncWeb.HubLive do
use IntisyncWeb, :live_view
alias IntisyncWeb.LiveViewMonitor
alias Intisync.SessionsSupervisor
def mount(_params, _session, socket) do
socket = assign(socket, :session_id, nil)
{:ok, socket}
end
def handle_params(%{"id" => session_id}, _uri, socket) do
if connected?(socket),
do: handle_connected(session_id, socket),
else: {:noreply, socket}
end
defp handle_connected(session_id, socket) do
socket = assign(socket, :session_id, session_id)
if exists_session?(session_id) do
socket = socket |> put_flash(:error, "Unauthorized") |> redirect(to: "/")
{:noreply, socket}
else
{:ok, pid} = SessionsSupervisor.start_session(session_id)
LiveViewMonitor.monitor(self(), __MODULE__, {pid})
{:noreply, assign(socket, :session_server_pid, pid)}
end
end
def unmount(_reason, {session_server_pid}) do
SessionsSupervisor.close_session(session_server_pid)
end
end
Also, it is important to note that I do the following on my Router:
defmodule IntisyncWeb.Router do
use IntisyncWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {IntisyncWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", IntisyncWeb do
pipe_through :browser
live "/", LobbyLive
live "/sessions/:id/remote", RemoteLive
live_session :hub, root_layout: {IntisyncWeb.Layouts, :hub_root} do
live "/sessions/:id", HubLive
end
end
end
end
For context, the SessionsSupervisor starts a gen server that contains the state of the session, and I want this session gen server to be terminated when this live view goes down. The reason I do this is because in this sessions you can invite other people that need to read this state, so thats why I have a gen server that stores this shared state ![]()
The problem here is that sometimes, when I refresh the HubLive page, the unmount function doesn’t seem to be called. I expected that when I refresh a page containing a live view the following happens: LiveView terminates → New live view is mounted
So that means, in some browsers when refreshing the page, the live view doesn’t die automatically (At least that is my hypothesis, I don’t know certainly)
Another thing could be that I in the router I put the HubLive under a live_session, and it might be that when navigating from a live view to the same live view under the same live_session that the live view doesn’t die?
The only reason I do this is because HubLive uses a different root layout than the other views and this is the first way I learnt how to assign a different root to a view, so if you know a better way let me know.
And last but not least, maybe I should somehow monitor the socket pid instead of the live view pid? How do I even do that? How do I get the pid of a socket?






















