Register process info on topic and replicate it across cluster.
Simple use case would be showing whose online
metadata should be minimized and used to store small, ephemeral state, such as a user's "online" or "away" status. Can fetch more substantial stuff by overriding the fetch/2 ft
defmodule SsWeb.Presence do
use Phoenix.Presence, otp_app: :ss,
pubsub_server: Ss.PubSub
end
lib/hello/application.ex
children = [
...
HelloWeb.Presence,
]
lib/channels/user_socket.ex - add user_id to socket
defmodule SsWeb.UserSocket do
use Phoenix.Socket
channel "room:*", SsWeb.RoomChannel
def connect(params, socket, _connect_info) do
{:ok, assign(socket, :user_id, params["user_id"])}
end
end
lib/channels/room_channel.ex
defmodule HelloWeb.RoomChannel do
use Phoenix.Channel
alias HelloWeb.Presence
# intercept ["presence_diff"]
def join("room:lobby", _message, socket) do
send(self(), :after_join)
{:ok, :hiii, socket}
end
def handle_info(:after_join, socket) do
push(socket, "presence_state", Presence.list(socket))
#Presence.track links channel's process as a presence for the socket's userID with map of metadata
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:second))
})
{:noreply, socket}
end
# To alter or stop presence updates
# def handle_out("presence_diff", msg, socket) do
# {:noreply, socket}
# end
end
Client
Actually (and before 1.4?) recieved "presence_diff" and "presence_state" events
Only one onSync handler allowed
import {Socket, Presence} from "phoenix"
let socket = new Socket("/socket", {
params: {user_id: window.location.search.split("=")[1]}
})
let channel = socket.channel("room:lobby", {})
let presence = new Presence(channel)
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
response += `<br>${id} (count: ${count})</br>`
}) //metas is list of users with this id
document.querySelector("main[role=main]").innerHTML = response
}
socket.connect()
presence.onSync(() => renderOnlineUsers(presence))
channel.join()
More Client
let presence = new Presence(channel)
// detect if user has joined for the 1st time or from another tab/device
presence.onJoin((id, current, newPres) => {
if(!current){
console.log("user has entered for the first time", newPres)
} else {
console.log("user additional presence", newPres)
}
})
// detect if user has left from all tabs/devices, or is still present
presence.onLeave((id, current, leftPres) => {
if(current.metas.length === 0){
console.log("user has left from all devices", leftPres)
} else {
console.log("user left from a device", leftPres)
}
})
// receive presence data from server
presence.onSync(() => {
displayUsers(presence.list())
})