We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
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.