feat(chat): use Phoenix.Presence to show online users
This commit is contained in:
parent
503efaf10b
commit
124e18d656
@ -8,6 +8,10 @@ defmodule Slax.Accounts do
|
|||||||
|
|
||||||
alias Slax.Accounts.{User, UserToken, UserNotifier}
|
alias Slax.Accounts.{User, UserToken, UserNotifier}
|
||||||
|
|
||||||
|
def list_users do
|
||||||
|
Repo.all(from(u in User, order_by: [asc: u.email]))
|
||||||
|
end
|
||||||
|
|
||||||
## Database getters
|
## Database getters
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -14,6 +14,7 @@ defmodule Slax.Application do
|
|||||||
{Phoenix.PubSub, name: Slax.PubSub},
|
{Phoenix.PubSub, name: Slax.PubSub},
|
||||||
# Start the Finch HTTP client for sending emails
|
# Start the Finch HTTP client for sending emails
|
||||||
{Finch, name: Slax.Finch},
|
{Finch, name: Slax.Finch},
|
||||||
|
SlaxWeb.Presence,
|
||||||
SlaxWeb.Endpoint
|
SlaxWeb.Endpoint
|
||||||
]
|
]
|
||||||
|
|
||||||
|
11
lib/slax_web/channels/presence.ex
Normal file
11
lib/slax_web/channels/presence.ex
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
defmodule SlaxWeb.Presence do
|
||||||
|
@moduledoc """
|
||||||
|
Provides presence tracking to channels and processes.
|
||||||
|
|
||||||
|
See the [`Phoenix.Presence`](https://hexdocs.pm/phoenix/Phoenix.Presence.html)
|
||||||
|
docs for more details.
|
||||||
|
"""
|
||||||
|
use Phoenix.Presence,
|
||||||
|
otp_app: :slax,
|
||||||
|
pubsub_server: Slax.PubSub
|
||||||
|
end
|
@ -4,10 +4,12 @@ defmodule SlaxWeb.ChatRoomLive do
|
|||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
alias Slax.Accounts
|
||||||
alias Slax.Accounts.User
|
alias Slax.Accounts.User
|
||||||
alias Slax.Chat
|
alias Slax.Chat
|
||||||
alias Slax.Chat.Message
|
alias Slax.Chat.Message
|
||||||
alias Slax.Chat.Room
|
alias Slax.Chat.Room
|
||||||
|
alias SlaxWeb.OnlineUsers
|
||||||
|
|
||||||
@impl Phoenix.LiveView
|
@impl Phoenix.LiveView
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
@ -25,6 +27,20 @@ defmodule SlaxWeb.ChatRoomLive do
|
|||||||
<div id="rooms-list">
|
<div id="rooms-list">
|
||||||
<.room_link :for={room <- @rooms} room={room} active={room.id == @room.id} />
|
<.room_link :for={room <- @rooms} room={room} active={room.id == @room.id} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div class="flex items-center h-8 px-3 group">
|
||||||
|
<div class="flex items-center flex-grow focus:outline-none">
|
||||||
|
<span class="ml-2 leading-none font-medium text-sm">Users</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="users-list">
|
||||||
|
<.user
|
||||||
|
:for={user <- @users}
|
||||||
|
user={user}
|
||||||
|
online={OnlineUsers.online?(@online_users, user.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-grow shadow-lg">
|
<div class="flex flex-col flex-grow shadow-lg">
|
||||||
@ -133,10 +149,26 @@ defmodule SlaxWeb.ChatRoomLive do
|
|||||||
@impl Phoenix.LiveView
|
@impl Phoenix.LiveView
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
rooms = Chat.list_rooms()
|
rooms = Chat.list_rooms()
|
||||||
|
users = Accounts.list_users()
|
||||||
|
|
||||||
timezone = get_connect_params(socket)["timezone"]
|
timezone = get_connect_params(socket)["timezone"]
|
||||||
|
|
||||||
{:ok, assign(socket, hide_topic?: false, rooms: rooms, timezone: timezone)}
|
if connected?(socket) do
|
||||||
|
OnlineUsers.track(self(), socket.assigns.current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
OnlineUsers.subscribe()
|
||||||
|
|
||||||
|
socket =
|
||||||
|
assign(socket,
|
||||||
|
hide_topic?: false,
|
||||||
|
online_users: OnlineUsers.list(),
|
||||||
|
rooms: rooms,
|
||||||
|
timezone: timezone,
|
||||||
|
users: users
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Phoenix.LiveView
|
@impl Phoenix.LiveView
|
||||||
@ -213,6 +245,12 @@ defmodule SlaxWeb.ChatRoomLive do
|
|||||||
{:noreply, stream_delete(socket, :messages, message)}
|
{:noreply, stream_delete(socket, :messages, message)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Phoenix.LiveView
|
||||||
|
def handle_info(%{event: "presence_diff", payload: diff}, socket) do
|
||||||
|
online_users = OnlineUsers.update(socket.assigns.online_users, diff)
|
||||||
|
{:noreply, assign(socket, online_users: online_users)}
|
||||||
|
end
|
||||||
|
|
||||||
attr :active, :boolean, required: true
|
attr :active, :boolean, required: true
|
||||||
attr :room, Room, required: true
|
attr :room, Room, required: true
|
||||||
|
|
||||||
@ -234,6 +272,24 @@ defmodule SlaxWeb.ChatRoomLive do
|
|||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr :user, User, required: true
|
||||||
|
attr :online, :boolean, default: false
|
||||||
|
|
||||||
|
defp user(assigns) do
|
||||||
|
~H"""
|
||||||
|
<.link class="flex items-center h-8 hover:bg-gray-300 text-sm pl-8 pr-3" href="#">
|
||||||
|
<div class="flex justify-center w-4">
|
||||||
|
<%= if @online do %>
|
||||||
|
<span class="w-2 h-2 rounded-full bg-blue-500"></span>
|
||||||
|
<% else %>
|
||||||
|
<span class="w-2 h-2 rounded-full border-2 border-gray-500"></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<span class="ml-2 leading-none"><%= username(@user) %></span>
|
||||||
|
</.link>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
attr :current_user, User, required: true
|
attr :current_user, User, required: true
|
||||||
attr :dom_id, :string, required: true
|
attr :dom_id, :string, required: true
|
||||||
attr :message, Message, required: true
|
attr :message, Message, required: true
|
||||||
|
48
lib/slax_web/online_users.ex
Normal file
48
lib/slax_web/online_users.ex
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
defmodule SlaxWeb.OnlineUsers do
|
||||||
|
alias Phoenix.Presence
|
||||||
|
alias SlaxWeb.Presence
|
||||||
|
|
||||||
|
@topic "online_users"
|
||||||
|
|
||||||
|
def list do
|
||||||
|
@topic
|
||||||
|
|> Presence.list()
|
||||||
|
|> Enum.into(
|
||||||
|
%{},
|
||||||
|
fn {id, %{metas: metas}} ->
|
||||||
|
{String.to_integer(id), length(metas)}
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def track(pid, user) do
|
||||||
|
with {:ok, _} <- Presence.track(pid, @topic, user.id, %{}) do
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def online?(online_users, user_id) do
|
||||||
|
Map.get(online_users, user_id, 0) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe do
|
||||||
|
Phoenix.PubSub.subscribe(Slax.PubSub, @topic)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(online_users, %{joins: joins, leaves: leaves}) do
|
||||||
|
online_users
|
||||||
|
|> process_updates(joins, &Kernel.+/2)
|
||||||
|
|> process_updates(leaves, &Kernel.-/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp process_updates(online_users, updates, operation) do
|
||||||
|
Enum.reduce(updates, online_users, fn {id, %{metas: metas}}, acc ->
|
||||||
|
Map.update(
|
||||||
|
acc,
|
||||||
|
String.to_integer(id),
|
||||||
|
length(metas),
|
||||||
|
&operation.(&1, length(metas))
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user