Got stuck using Phoenix Presence and LiveView for tracking signed in users

Hi guys,

I’m building a small app where I would like to have a page with listed users who are currently online across the app, no matter which page they are on at the moment.

I created a LiveView to track users and the page where the list of users is showing, but if new users sign in or sign out, the list is not being updated and I get the following message in terminal:

[debug] send_update failed because component MyAppWeb.PagesLive.OnlineUsersComponent with ID "online-users" does not exist or it has been removed

app.html.heex - I added LiveView inside app.html.heex

<div>
  <%= live_render(@socket, MyAppWeb.OMyAppnlineUsersLive, id: "online-users", sticky: true) %>

  <main>
    <%= @inner_content %>
  </main>
</div>

OnlineUsersLive LiveView

defmodule MyAppWeb.OnlineUsersLive do
  use MyAppWeb, :live_view
  alias MyAppWeb.Presence
  alias MyApp.{Accounts, Profiles}
  alias MyAppWeb.PagesLive.OnlineUsersComponent

  @impl true
  def render(assigns) do
    ~H"""
      <div class=""></div>
    """
  end

  @impl true
  def mount(_params, %{"user_token" => user_token} = _session, socket) do
    current_user = Accounts.get_user_by_session_token(user_token)
    profile = Profiles.get_profile!(current_user.id)

    if connected?(socket) do
      {:ok, _} = Presence.track_user(self(), current_user, profile)

      Phoenix.PubSub.subscribe(MyApp.PubSub, "online_users")
    end

    {:ok,
      socket
      |> assign(:current_user, current_user),
      layout: false
    }
  end

  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "online_users")
    end
    {:ok,
      socket
      |> assign(:current_user, nil),
      layout: false
    }
  end

  @impl true
  def handle_info(%{event: "presence_diff"}, socket) do
    send_update(OnlineUsersComponent, id: "online-users")

    {:noreply, socket}
  end
end

presence.ex

defmodule MyAppWeb.Presence do
  use Phoenix.Presence, otp_app: :myapp, pubsub_server: MyApp.PubSub

  alias __MODULE__

  def track_user(pid, user, profile) do
    Presence.track(
      pid,
      "online_users",
      user.id,
      %{
        username: profile.username,
        name: profile.name,
      }
    )
  end
end

Online Users Component

defmodule MyAppWeb.PagesLive.OnlineUsersComponent do
  use MyAppWeb, :live_component

  alias MyAppWeb.Presence

  @impl true
  def render(assigns) do
    ~H"""
      <div id={@id}>
        Online Users
        <%= for {user_id, %{metas: [data]}} <- @online_users do %>
          <%= user_id %>
          <%= data.username %>
        <% end %>
      </div>
    """
  end

  @impl true
  def update(assigns, socket) do
    {:ok,
      socket
      |> assign(assigns)
      |> assign(:online_users, Presence.list("online_users"))
    }
  end
end

browse.ex page where signed in users are listed

<div>
Browse Users

<.live_component
  module={MyAppWeb.PagesLive.OnlineUsersComponent}
  id="online-users" 
/>
</div>

Can anyone help?

Are you running the component in the same Liveview? Reading through the post I’m not 100% sure (or I’ve missed something), but if you check Phoenix.LiveView — Phoenix LiveView v0.20.14 specifically, the following:

The pid argument is optional and it defaults to the current process, which means the update instruction will be sent to a component running on the same LiveView. If the current process is not a LiveView or you want to send updates to a live component running on another LiveView, you should explicitly pass the LiveView’s pid instead.

No, it’s another LiveView, sorry for not mentioning it. How do I get pid of another LiveView?

I’ve just started working on some presence tracking myself and whilst I’ve only been looking through the documentation at this stage, it might be worth looking into Phoenix Channels and PubSub i.e. have your target liveview subscribed to a topic that receives broadcasts of updates.

There’s some useful information here that I’ve been using to get myself started: Channels — Phoenix v1.7.11

I’ve found a post that goes into it in much more detail here: Tracking online users with Presence