drab v0.10.5 Drab.Presence View Source
Conveniences for Phoenix.Presence
.
Provides Phoenix Presence module for Drab, along with some helper functions.
Installation
It is disabled by default, to enable it, add :presence
to config.exs
.
config :drab, :presence, true
Next, add Drab.Presence
to your supervision tree in lib/my_app_web.ex
:
children = [
...
Drab.Presence,
]
Please also ensure that there otp_app
and endpoint for this app are configured correctly:
config :drab, MyAppWeb.Endpoint,
otp_app: :my_app_web
In multiple endpoint configuration, you need to specify which endpoint to use with
Drab.Presence
:
config :drab, :presence, endpoint: MyAppWeb.Endpoint
Usage
When installed, system tracks the presence over every Drab topic, both static topics configured
by Drab.Commander.broadcasting/1
and runtime topics run by Drab.Commander.subscribe/2
.
The default ID of the presence list is a browser UUID (Drab.Browser.id/1
):
iex> Drab.Presence.list socket
%{
"2bd34ffc-b365-46a9-9479-474b628364ed" => %{
metas: [%{online_at: 1520417565, phx_ref: ...}]
}
}
The ID may also be taken from Plug Session or from the Drab Store. For example, you have
a :current_user_id
stored in the session, you may want to use it as an id with
id
config option:
config :drab, :presence, id: [session: :current_user_id]
So this ID will become the key of the presence map:
iex> Drab.Presence.list socket
%{
"42" => %{
metas: [%{online_at: 1520417565, phx_ref: ...}]
}
}
Notice that system transforms the key value to the binary string.
The similar would be with the Drab Store:
config :drab, :presence, id: [store: :current_user_id]
There is also a possibility to specify both store and session. In this case the order matters: if you put store first, it will take a value from the store, and it is not found, from session.
config :drab, :presence, id: [store: :current_user_id, session: :current_user_id]
config :drab, :presence, id: [session: :current_user_id, store: :current_user_id]
Example
Here we are going to show how to display number of connected users online. The solution is to broadcast every connect and disconnect using Commander’s callbacks. Thus, in the commander:
defmodule MyAppWeb.MyCommander
use Drab.Commander
import Drab.Presence
broadcasting "global"
onconnect :connected
ondisconnect :disconnected
def connected(socket) do
broadcast_html socket, "#number_of_users", count_users(socket)
end
def disconnected(_store, _session) do
topic = same_topic("global")
broadcast_html topic, "#number_of_users", count_users(topic)
end
end
Notice the difference between connected
and disconnected
callbacks. In the first case
we could use the default topic derived from the socket
, but after disconnect socket does not
longer exists.
Own Presence module
You may also want to provide your own presence module, for example to override
Phoenix.Presence.fetch/2
function. In this case, add your module to children
list and
configure Drab to run it:
config :drab, :presence, module: MyAppWeb.MyPresence
You module must provide start/2
function, which will be launched by Drab on the client connect,
along with stop/2
, which runs when user unsubscribe from the topic.
defmodule MyAppWeb.MyPresence do
use Phoenix.Presence, otp_app: :my_app, pubsub_server: MyApp.PubSub
def start(socket, topic) do
client_id = Drab.Browser.id!(socket)
track(socket.channel_pid, topic, client_id, %{online_at: System.system_time(:seconds)})
end
def stop(socket, topic) do
client_id = Drab.Browser.id!(socket)
untrack(socket.channel_pid, topic, client_id)
end
end
Link to this section Summary
Functions
Returns the number of total connections to the topic
Counts the number of connected unique users or browsers
Extend presence information with additional data
Callback implementation for Phoenix.Presence.handle_diff/2
Callback implementation for Phoenix.Presence.init/1
Returns presences for a channel
Callback implementation for Phoenix.Presence.start_link/1
Track a channel’s process as a presence
Track an arbitary process as a presence
Stop tracking a channel’s process
Stop tracking a process
Update a channel presence’s metadata
Update a process presence’s metadata
Link to this section Functions
count_connections(Phoenix.Socket.t() | String.t()) :: integer()
Returns the number of total connections to the topic.
count_users(Phoenix.Socket.t() | String.t()) :: integer()
Counts the number of connected unique users or browsers.
Extend presence information with additional data.
When list/1
is used to list all presences of the given topic
, this
callback is triggered once to modify the result before it is broadcasted to
all channel subscribers. This avoids N query problems and provides a single
place to extend presence metadata. You must return a map of data matching the
original result, including the :metas
key, but can extend the map to include
any additional information.
The default implementation simply passes presences
through unchanged.
Example
def fetch(_topic, presences) do
query =
from u in User,
where: u.id in ^Map.keys(presences),
select: {u.id, u}
users = query |> Repo.all() |> Enum.into(%{})
for {key, %{metas: metas}} <- presences, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
end
Callback implementation for Phoenix.Presence.fetch/2
.
Callback implementation for Phoenix.Presence.handle_diff/2
.
Callback implementation for Phoenix.Presence.init/1
.
Returns presences for a channel.
Calls list/2
with presence module.
Callback implementation for Phoenix.Presence.list/1
.
Callback implementation for Phoenix.Presence.start_link/1
.
Track a channel’s process as a presence.
Tracked presences are grouped by key
, cast as a string. For example, to
group each user’s channels together, use user IDs as keys. Each presence can
be associated with a map of metadata to store small, emphemeral state, such as
a user’s online status. To store detailed information, see fetch/2
.
Example
alias MyApp.Presence
def handle_info(:after_join, socket) do
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:second))
})
{:noreply, socket}
end
Callback implementation for Phoenix.Presence.track/3
.
Track an arbitary process as a presence.
Same with track/3
, except track any process by topic
and key
.
Callback implementation for Phoenix.Presence.track/4
.
Stop tracking a channel’s process.
Callback implementation for Phoenix.Presence.untrack/2
.
Stop tracking a process.
Callback implementation for Phoenix.Presence.untrack/3
.
Update a channel presence’s metadata.
Replace a presence’s metadata by passing a new map or a function that takes the current map and returns a new one.
Callback implementation for Phoenix.Presence.update/3
.
Update a process presence’s metadata.
Same as update/3
, but with an arbitary process.
Callback implementation for Phoenix.Presence.update/4
.