Phoenix.Presence

Parsed documentation:
View on GitHub
Provides Presence tracking to processes and channels.

This behaviour provides presence features such as fetching
presences for a given topic, as well as handling diffs of
join and leave events as they occur in real-time. Using this
module defines a supervisor and allows the calling module to
implement the `Phoenix.Tracker` behaviour which starts a
tracker process to handle presence information.

## Example Usage

Start by defining a presence module within your application
which uses `Phoenix.Presence` and provide the `:otp_app` which
holds your configuration, as well as the `:pubsub_server`.

    defmodule MyApp.Presence do
      use Phoenix.Presence, otp_app: :my_app,
                            pubsub_server: MyApp.PubSub
    end

The `:pubsub_server` must point to an existing pubsub server
running in your application, which is included by default as
`MyApp.PubSub` for new applications.

Next, add the new supervisor to your supervision tree in `lib/my_app.ex`:

    children = [
      ...
      MyApp.Presence,
    ]

Once added, presences can be tracked in your channel after joining:

    defmodule MyApp.MyChannel do
      use MyAppWeb, :channel
      alias MyApp.Presence

      def join("some:topic", _params, socket) do
        send(self(), :after_join)
        {:ok, assign(socket, :user_id, ...)}
      end

      def handle_info(:after_join, socket) do
        push(socket, "presence_state", Presence.list(socket))
        {:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
          online_at: inspect(System.system_time(:second))
        })
        {:noreply, socket}
      end
    end

In the example above, the current presence information for
the socket's topic is pushed to the client as a `"presence_state"` event.
Next, `Presence.track` is used to register this
channel's process as a presence for the socket's user ID, with
a map of metadata.

Finally, a diff of presence join and leave events will be sent to the
client as they happen in real-time with the "presence_diff" event.
The diff structure will be a map of `:joins` and `:leaves` of the form:

    %{joins: %{"123" => %{metas: [%{status: "away", phx_ref: ...}]},
      leaves: %{"456" => %{metas: [%{status: "online", phx_ref: ...}]},

See `Phoenix.Presence.list/2` for more information on the presence
data structure.

## Fetching Presence Information

Presence metadata should be minimized and used to store small,
ephemeral state, such as a user's "online" or "away" status.
More detailed information, such as user details that need to
be fetched from the database, can be achieved by overriding the `fetch/2`
function. The `fetch/2` callback is triggered when using `list/1`
and serves as a mechanism to fetch presence information a single time,
before broadcasting the information to all channel subscribers.
This prevents N query problems and gives you a single place to group
isolated data fetching to extend presence metadata. The function must
return a map of data matching the outlined Presence data structure,
including the `:metas` key, but can extend the map of information
to include any additional information. For 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

The function above fetches all users from the database who
have registered presences for the given topic. The fetched
information is then extended with a `:user` key of the user's
information, while maintaining the required `:metas` field from the
original presence data.
No suggestions.
Please help! Open an issue on GitHub if this assessment is incorrect.