Home Posts Post Search Tag Search

LiveView 26 - Chapter 9: Your Turn
Published on: 2026-01-25 Tags: elixir, Blog, Side Project, LiveView, Phoenix

Your Turn

You did it, think about all the ways that you could use this feature in your code. Could you find the average number of people that browse a certain feature? It’s a simple enough task to implement.

Give It a Try


• Use PubSub and Presence to track the number of people taking a survey, then add a new component to the admin dashboard view to display this total list of survey-taking users


For this I will need to add in a tracker for the survey_live.ex so that when it mounts it adds in an other count to the total count. I added it to the mount as I want to be sure that if anyone is looking anywhere on the survey_live.ex it will add in a new user to the Presence tracker


@impl true
  def mount(_params, _session, socket) do
    maybe_track_survey(socket)

    socket =
      socket
      |> assign_demographic()
      |> assign_products()

    {:ok, socket}
  end

  ...

  defp maybe_track_survey(socket) do
    if connected?(socket) do
      user = socket.assigns.current_scope.user
      Presence.track_survey(self(), user)
    end
  end

This is responsible for making sure that every time a user is looking at the page we are adding them to the list


Next we need to add in the new function within the PentoWeb.Presence.ex that will take care of the tracking of the user. This is where I think for the single data point that we want to track it might be overkill


  def track_survey(pid, user) do
    track(pid, @survey_activity_topic, user.id, %{username: user.username})
  end

  def show_number_of_surveys() do
    list(@survey_activity_topic)
    |> Enum.map(&extract_user_user_name/1)
    |> Enum.count()
  end

  defp extract_user_user_name({user_id, %{metas: metas}}) do
    {user_id, get_in(metas, [:users])}
  end

As you can see we are going to keep track of the user.id and then all the information about the user. This is more than I need but I wasn’t sure how to track less, as track/4 needs 4 params


Next I needed to create the survey_activity_live.ex component that will be responsible for showing the number or users filling out a survey (or viewing theirs)


defmodule PentoWeb.Admin.SurveyActivityLive do
  use PentoWeb, :live_component
  alias PentoWeb.Presence

  @impl true
  def render(assigns) do
    ~H"""
    <div class="user-activity-component ml-8">
      <h2>Survey Activity</h2>
      <p>Active users currently taking a survey</p>
      <div> {@survey_activity} </div>
    </div>
    """
  end

  @impl true
  def update(_assigns, socket) do
    {
      :ok,
      socket
      |> assign_survey_activity()
    }
  end

  def assign_survey_activity(socket) do
    assign(socket, :survey_activity, Presence.show_number_of_surveys())
  end
end

This will leverage the PentoWeb.Presence module that we created. With all of this done we now only need to add the new component to the Admin.DashboardLive.ex live view. You will need to add in the Endpoint.subscribe/1 (add in the new assign too), the alias, the global topic, add in the html to use the new component, as well as the add in more for the handle_info/2 event to make sure that we update both components


defmodule PentoWeb.Admin.DashboardLive do
  ...
  alias PentoWeb.Admin.{SurveyResultsLive, UserActivityLive, SurveyActivityLive}
  ...
  @survey_activity_topic "survey_activity"

  @impl true
  def render(assigns) do
    ~H"""
    <div class="container mx-auto p-6">
      <h1 class="text-3xl font-bold mb-6">Admin Dashboard</h1>
      <.live_component
        module={PentoWeb.Admin.SurveyResultsLive}
        id={@survey_results_component_id}
      />
      <.live_component
        module={PentoWeb.Admin.UserActivityLive}
        id={@user_activity_component_id}
      />
      <.live_component
      module={PentoWeb.Admin.SurveyActivityLive}
      id={@survey_activity_component_id}
      />
    </div>
    """
  end

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Endpoint.subscribe(@survey_results_topic)
      Endpoint.subscribe(@user_activity_topic)
      Endpoint.subscribe(@survey_activity_topic)
    end

    {:ok,
     socket
     |> assign(:survey_results_component_id, "survey-results")
     |> assign(:user_activity_component_id, "user-activity")
     |> assign(:survey_activity_component_id, "survey-activity")}
  end

  ...

  @impl true
  def handle_info(%{event: "presence_diff"}, socket) do
    refresh_components([
      {UserActivityLive, socket.assigns.user_activity_component_id},
      {SurveyActivityLive, socket.assigns.survey_activity_component_id}
    ])

    {:noreply, socket}
  end

  defp refresh_components(components) do
    Enum.each(components, fn {module, id} ->
      send_update(module, id: id)
    end)
  end
end

• What happens when a user navigates away from a survey page? Did your list of survey-taking users update on its own, without you writing any new code to support this feature? Think through why this is


When a user navigates away from a survey page, the list of survey-taking users updates automatically, even without writing extra code. This happens because Presence is tracking the user’s PID—the process handling their LiveView. Once the user leaves the page, that LiveView process terminates, and Presence detects the PID is gone. As a result, it automatically removes the user from the tracking list


In short, once you track something with Presence, it continuously monitors the PID for changes. When the PID ends, the presence data updates on its own.